Reputation: 2473
Recently I read "Java concurrency in practice" Section --> "3.4.2 Example: Using volatile to publish immutable objects".
However; I can't quietly understand it. Here is the situation!
Immutable object is:
@Immutable
class OneValueCache {
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
public OneValueCache(BigInteger i, BigInteger[] factors) {
lastNumber = i;
lastFactors = Arrays.copyOf(factors, factors.length);
}
public BigInteger[] getFactors(BigInteger i) {
if (lastNumber == null || !lastNumber.equals(i))
return null;
else
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}
Suppose we have a servlet like this
public class MyServlet {
private volatile OneValueCache immutableValue;
.
.
public void service(){
.
// what this function do : gets value from immutable object increments its
// value and creates new Immutable object and put it back.
//
.
immutableValue = new OneValueCache( lastNumber, lastFactor[]);
.
.
.
}
.
.
.
}
According the book MyServlet is Thread Safe because it publishes Immutable Object with volatile keyword!
However I think there is a problem here!
Suppose:
Thread 1 in time 0 read the values from immutableValue to its stack var;
Thread 2 in time 0 read the values from immutableValue to its stack var;
Thread 1 in time 1 increment the values in stack variable and create new OneValueCache object -which is immutable - and put it in immutableValue field variable;
Thread 2 in time 2 increment the values in stack variable and create new OneValueCache object -which is immutable - and put it in immutableValue field variable;
This way we lost Thread1 update so there is no ThreadSafty here!
On the contrary the book says:
" This combination of an immutable holder object for multiple state variables related by an invariant, and a volatile reference used to ensure its timely visibility, allows VolatileCachedFactorizer to be thread-safe even though it does no explicit locking. "
Can anyone help me what am I missing here?
p.s: to more clarify, I know data consistency is held here. we can not have invalid OneValueCache state. but there is a chance for lost update here as my thread sequence shows. so the class is not thread safe!
Upvotes: 2
Views: 2556
Reputation: 21
volatile keyword ensures that threads pull latest version of immutable object from shared memory rather than from a local cache (ex: cpu register, stack, etc.). However, if two threads read, modify and publish the object simultaneously to the same volatile variable, changes from one thread can get lost. To support such use cases, it is better to use mutex like synchronized keyword or a read-write lock.
Typically, you would use volatile when only one thread publishes updated immutable objects to that volatile variable, but multiple threads read from it. In such cases, you do not worry about thread interference.
In Java, threads can cache (partial/full) copies of non-volatile variables into their own "reserved" memory space (ex: cpu register, stack, etc.). volatile keyword can be seen as a way to bypass these caches and ensure that thread gets fresh copy of object when you access and only when you access the volatile variable.
Upvotes: 2
Reputation: 7940
Immutability implies thread safety because once created no thread can modify it, so value will remain consistent across threads.
In the above example "private volatile OneValueCache immutableValue" can hold only one value at a time , so if thread 1 updated first and then thread 2 updated reference value then the last value will represented by "onevaluecache" and update of Thread 1 will be lost as you are storing only one reference value.
At no point in steps mentioned by you state of OneValueCache is inconsistent, so it is thread safe.
EDIT: Some of Cases where state will not be consistent are:
If immutableValue variable was not volatile then each thread will store its own copy, which means cache will not work properly.
If OneValueCache class was mutable then mutiple threads will look to change its value simultaneously which means state will not be consistent hence not thread safe.
Upvotes: 3