JDK-4808233 : "Locale" not thread-safe
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:i18n
  • Affected Version: 1.4.1,7
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux,windows_xp
  • CPU: x86
  • Submitted: 2003-01-25
  • Updated: 2011-12-26
  • Resolved: 2011-12-26
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8 b19Fixed
Related Reports
Duplicate :  
Description

Name: rmT116609			Date: 01/24/2003


FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)


FULL OPERATING SYSTEM VERSION :
Microsoft Windows XP [version 5.1.2600]


A DESCRIPTION OF THE PROBLEM :
java.util.Locale seems not to be thread safe, as I look at the source code.

The static method getDefault() is not synchronized. The code is as follows:

public static Locale getDefault() {
        // do not synchronize this method - see 4071298
        // it's OK if more than one default locale happens to be created
        if (defaultLocale == null) {
            // ... do something ...
            defaultLocale = new Locale(language, country, variant);
        }
        return defaultLocale;
    }

This method seems to have been synchronized in the past, but the bug report 4071298 removed the "synchronized" modifier.

The problem is that for multiprocessor machines, each processor having its own cache, the data in these caches are never synchronized with the main memory.
The lack of a memory barrier, that is provided normally by the "synchronized" modifier, can make a thread read an incompletely initialized Locale instance referenced by the static private variable "defaultlocale".

This problem is well explained in
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html and other documents about multithreading.

I think this method must just be synchronized again.


(Review ID: 179833) 
======================================================================

Comments
EVALUATION Moved the "defaultLocale" initialization to static initializer, as well as making it a volatile field. Also, "defaultDisplayLocale" and "defaultFormatLocale" are made volatile, and initialized using double check locking.
13-12-2011

SUGGESTED FIX http://hg.openjdk.java.net/jdk8/tl/jdk/rev/c647ebb3c4f7
13-12-2011

SUGGESTED FIX *** /tmp/geta15960 Fri Feb 21 01:56:22 2003 --- Locale.java Fri Feb 21 01:55:43 2003 *************** *** 309,341 **** * @return the default locale for this instance of the Java Virtual Machine */ public static Locale getDefault() { - // do not synchronize this method - see 4071298 - // it's OK if more than one default locale happens to be created - if (defaultLocale == null) { - String language, region, country, variant; - language = (String) AccessController.doPrivileged( - new GetPropertyAction("user.language", "en")); - // for compatibility, check for old user.region property - region = (String) AccessController.doPrivileged( - new GetPropertyAction("user.region")); - if (region != null) { - // region can be of form country, country_variant, or _variant - int i = region.indexOf('_'); - if (i >= 0) { - country = region.substring(0, i); - variant = region.substring(i + 1); - } else { - country = region; - variant = ""; - } - } else { - country = (String) AccessController.doPrivileged( - new GetPropertyAction("user.country", "")); - variant = (String) AccessController.doPrivileged( - new GetPropertyAction("user.variant", "")); - } - defaultLocale = new Locale(language, country, variant); - } return defaultLocale; } --- 309,314 ---- *************** *** 914,920 **** */ private int hashcode = -1; // lazy evaluated ! private static Locale defaultLocale = null; /** * Return an array of the display names of the variant. --- 887,919 ---- */ private int hashcode = -1; // lazy evaluated ! private volatile static Locale defaultLocale; ! ! static { ! String language, region, country, variant; ! language = (String) AccessController.doPrivileged( ! new GetPropertyAction("user.language", "en")); ! // for compatibility, check for old user.region property ! region = (String) AccessController.doPrivileged( ! new GetPropertyAction("user.region")); ! if (region != null) { ! // region can be of form country, country_variant, or _variant ! int i = region.indexOf('_'); ! if (i >= 0) { ! country = region.substring(0, i); ! variant = region.substring(i + 1); ! } else { ! country = region; ! variant = ""; ! } ! } else { ! country = (String) AccessController.doPrivileged( ! new GetPropertyAction("user.country", "")); ! variant = (String) AccessController.doPrivileged( ! new GetPropertyAction("user.variant", "")); ! } ! defaultLocale = new Locale(language, country, variant); ! } /** * Return an array of the display names of the variant. ###@###.### 2003-02-21
21-02-2003

EVALUATION Will consider this bug for Tiger. ###@###.### 2003-01-29 When starting JVM, Locale.getDefault() is always invoked. It doesn't make sense to check if defaultLocale is null. We can just initialize it at the class loading time. ###@###.### 2003-02-21
21-02-2003