According to the Java Language Specification [JLS 20052011], Section 8 §8.3.1.4, "volatile
Fields","
A field may be declared
volatile
, in which case the Java memory model (§17) Memory Model ensures that all threads see a consistent value for the variable (§17.4).
This visibility guarantee applies only to primitive fields and object references. Programmers commonly use imprecise terminology and speak about "member objects." For the purposes of this visibility guarantee, the actual member is the object reference; the objects referred to (hereafter known as the referents) by volatile object references are beyond the scope of the visibility guarantee. Consequently, declaring an object reference to be volatile is insufficient to guarantee that changes to the members of the referent are visible. That is, a thread may fail to observe a recent write from another thread to a member field of such an object referent. Furthermore, when the referent is mutable and lacks thread-safety, other threads might see a partially constructed object or an object in a (temporarily) inconsistent state [Goetz 2007]. However, if the referent is immutable, declaring the reference volatile suffices to guarantee visibility of the members of the referent. Consequently, programmers must not use the volatile
keyword to guarantee visibility to mutable objects; use of the volatile
keyword to guarantee visibility only to primitive fields, object references, or fields of immutable object referents is permitted.
...
The root of the problem is that the thread that calls setFirst()
and the thread that calls getFirst()
lack a happens-before relationship. A happens-before relationship exists between a thread that writes to a volatile variable and a thread that subsequently reads it. However, setFirst()
and getFirst()
only read from a volatile variableâ”the variable—the volatile reference to the array; neither method writes to the volatile variable.
...
AtomicIntegerArray
guarantees a a happens-before relationship between a thread that calls atomicArray.set()
and a thread that subsequently calls atomicArray.get()
.
...
Synchronization establishes a a happens-before relationship between threads that synchronize on the same lock. In this case, the thread that calls setFirst()
and the thread that subsequently calls getFirst()
both synchronize on the Foo
instance, so visibility is guaranteed.
...
The volatile-read, synchronized-write technique uses synchronization to preserve atomicity of compound operations, such as increment, and provides faster access times for atomic reads. However, it does not work with mutable objects because the visibility guarantee provided by volatile
extends only to the field itself (the primitive value or object reference); the referent (and hence the referent's members) is excluded from the guarantee. Consequently, the write and a subsequent read of the property lack a a happens-before relationship.
This technique is also discussed in VNA02-J. Ensure that compound operations on shared variables are atomic.
...
Because DateFormat
is not thread-safe [API 20062011] Class DateFormat, the value for Date
returned by the parse()
method might fail to correspond to the str
argument.
...
This compliant solution creates and returns a new DateFormat
instance for each invocation of the parse()
method [API 20062011] Class DateFormat.
Code Block | ||
---|---|---|
| ||
final class DateHandler { public static java.util.Date parse(String str) throws ParseException { return DateFormat.getDateInstance(DateFormat.MEDIUM).parse(str); } } |
...
This compliant solution makes DateHandler
thread-safe by synchronizing statements within the parse()
method [API 20062011] Class DateFormat.
Code Block | ||
---|---|---|
| ||
final class DateHandler { private static DateFormat format = DateFormat.getDateInstance(DateFormat.MEDIUM); public static java.util.Date parse(String str) throws ParseException { synchronized (format) { return format.parse(str); } } } |
...
Code Block | ||
---|---|---|
| ||
final class DateHandler { private static final ThreadLocal<DateFormat> format = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return DateFormat.getDateInstance(DateFormat.MEDIUM); } }; // ... } |
Exceptions
Applicability
Incorrectly assuming that declaring a field volatile guarantees the visibility of a referenced object's members can cause threads to observe stale or inconsistent values.
CON50-EX0: Technically, strict immutability of the referent is a stronger condition than is fundamentally required for safe visibility. When it can be determined that a referent is thread-safe by design, the field that holds its reference may be declared volatile. However, this approach to using volatile
decreases maintainability and should be avoided.
Risk Assessment
Incorrectly assuming that declaring a field volatile guarantees the visibility of a referenced object's members can cause threads to observe stale or inconsistent values.
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON50-JG | medium | probable | medium | P8 | L2 |
Bibliography
Class java.text. DateFormat | |
Pattern #2: "One-time safe publication" | |
Mutable Statics |
...