A common misconception is that shared references to immutable objects are immediately visible across multiple threads as soon as they are updated. For example, a developer can mistakenly believe that a class containing fields that refer only to immutable objects is itself immutable and consequently threa thread-safe.
Section 14.10.2, "Final Fields and Security," of Java Programming Language, Fourth Edition [JPL 2006] states:
...
References to both immutable and mutable objects must be made visible to all the threads. Immutable objects can be shared safely among multiple threads. However, references to mutable objects can be made visible before the objects are fully constructed. Rule TSM03-J. Do not publish partially initialized objects describes object construction and visibility issues specific to mutable objects.
...
The getHelper() method publishes the mutable helper field. Because the Helper class is immutable, it cannot be changed after it is initialized.
. Furthermore, because Helper is immutable, it is always constructed properly before its reference is made visible, in compliance with rule TSM03-J. Do not publish partially initialized objects. Unfortunately, a separate thread could observe a stale reference in the helper field of the Foo class.
...
This compliant solution synchronizes the methods of the Foo class to ensure that no thread sees a stale Helper reference.:
| Code Block | ||
|---|---|---|
| ||
final class Foo {
private Helper helper;
public synchronized Helper getHelper() {
return helper;
}
public synchronized void setHelper(int num) {
helper = new Helper(num);
}
}
|
The immutable Helper class remains unchanged.
Compliant Solution (
...
volatile)
References to immutable member objects can be made visible by declaring them volatile.:
| Code Block | ||
|---|---|---|
| ||
final class Foo {
private volatile Helper helper;
public Helper getHelper() {
return helper;
}
public void setHelper(int num) {
helper = new Helper(num);
}
}
|
...
This compliant solution wraps the mutable reference to the immutable Helper object within an AtomicReference wrapper that can be updated atomically.:
| Code Block | ||
|---|---|---|
| ||
final class Foo {
private final AtomicReference<Helper> helperRef =
new AtomicReference<Helper>();
public Helper getHelper() {
return helperRef.get();
}
public void setHelper(int num) {
helperRef.set(new Helper(num));
}
}
|
...
The incorrect assumption that classes that contain only references to immutable objects are themselves immutable can cause serious thread-safety issues.
Rule | Severity | Likelihood |
|---|
Detectable | Repairable | Priority | Level |
|---|---|---|---|
VNA01-J | Low |
Probable |
Yes |
No | P4 | L3 |
Automated Detection
Some static analysis tools are capable of detecting violations of this rule.
| Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Klocwork |
| SV.SHARED.VAR | |||||||
| Parasoft Jtest |
| CERT.VNA01.SGAS | Use the synchronized keyword on both the getter and setter methods, or on neither | ||||||
| ThreadSafe |
| CCE_SL_INCONSISTENT | Implemented |
...
| SonarQube |
| S2886 | Getters and setters should be synchronized in pairs |
Bibliography
Issue Tracking
| Tasklist | ||||
|---|---|---|---|---|
| ||||
||Completed||Priority||Locked||CreatedDate||CompletedDate||Assignee||Name|| |F|M|F|1270826173609| |dmohindr|"Unfortunately, a separate thread -could- *can* observe a stale reference in the helper field of the Foo class."| |T|M|F|1270826698362|1271441478121|svoboda|"This compliant solution synchronizes the methods of *class* Foo -class- " (it sounds strange with class occuring after Foo)| |
...
...