Reputation: 6209
I am reading about immutable objects and thread safety in Java Concurrency Guidelines by Fred Long.
Here is a code snippet from the book.
// Immutable Helper
public final class Helper {
private final int n;
public Helper(int n) {
this.n = n;
}
// ...
}
// and a mutable Foo class:
final class Foo {
private Helper helper;
public Helper getHelper() {
return helper;
}
public void setHelper(int num) {
helper = new Helper(num);
}
}
The code snippet is followed by the explanation:
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 guideline “TSM03-J. Do not publish partially initialized objects” on page 162.
Now let's open page 162 they've mentioned. Here is another code snippet.
class Foo {
private Helper helper;
public Helper getHelper() {
return helper;
}
public void initialize() {
helper = new Helper(42);
}
}
public class Helper {
private int n;
public Helper(int n) {
this.n = n;
}
}
It's followed by its own explanation:
If a thread accesses helper using the getHelper() method before the initialize() method has been called, the thread will observe an uninitialized helper field. Later, if one thread calls initialize() and another calls getHelper(), the second thread might observe one of the following:
- the helper reference as NULL,
- a fully initialized Helper object with the n field set to 42,
- a partially initialized Helper object with an uninitialized n that contains the default value 0.
So don't the two statements I made bold, contradict each other?
A wrote a piece of code to test the case, ran it a few times, and never got 0. Only null or 42. Here is my code:
package com.sample;
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
Foo foo = new Foo();
Initer initer = new Initer(foo);
Getter getter = new Getter(foo);
initer.start();
getter.start();
}
}
static class Getter extends Thread {
private Foo foo;
Getter(Foo foo) {
this.foo = foo;
}
@Override
public void run() {
System.out.println(foo.getHelper());
}
}
static class Initer extends Thread {
private Foo foo;
Initer(Foo foo) {
this.foo = foo;
}
@Override
public void run() {
foo.initialize();
}
}
static class Foo {
private Helper helper;
public Helper getHelper() {
return helper;
}
public void initialize() {
helper = new Helper(42);
}
}
public static class Helper {
private int n;
public Helper(int n) {
this.n = n;
}
@Override
public String toString() {
return Integer.toString(n);
}
}
}
Upvotes: 3
Views: 109
Reputation: 3795
The two statements are not inconsistent. If the first example the field n
is final
. This is what makes the Helper
class immutable. In the second example, n
is not final, Helper
is not immutable, which means a partially initialized instance could be returned.
As far as observing this with your code, Peter is right, it is difficult to test in practice for several reasons.
Upvotes: 2
Reputation: 533870
Unless the field is final
there is no guarantee the value will be visible however,
println
which has it's own memory barriers which will make it harder to see any inconsistency.Upvotes: 1