I had an example from the book 'java concurrency pratique', who says that volatile and immutable holder object gives thread safety. But I do not understand the example given by the book.
The code is as follows:
public class VolatileCachedFactorizer extends GenericServlet implements Servlet {
private volatile OneValueCache cache = new OneValueCache(null, null);
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = cache.getFactors(i);
if (factors == null) {
factors = factor(i); //----------> thread A
cache = new OneValueCache(i, factors); //---------> thread B
}
encodeIntoResponse(resp, factors);
}
}
public class OneValueCache {
private final BigInteger lastNum;
private final BigInteger[] lastFactors;
public OneValueCache(BigInteger i, BigInteger[] lastFactors){
this.lastNum = i;
this.lastFactors = lastFactors;
}
public BigInteger[] getFactors(BigInteger i){
if(lastNum == null || !lastNum.equals(i))
return null;
else
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}
I understand that
The key word volatile assure that the filed cache is visible to all threads.
The class OneValueCache is immutable. But we can change the reference of the variable cache.
But I cannot understand why the class VolatileCachedFactorizer is thread safe.
For two threads (Thread A and Thread B), if thread A and thread B arrive at factors == null
at the same time, the two threads A and B will both try to create the OneValueCache. Then Thread A arrives at factors = factor(i)
while threadB arrives at cache = new OneValueCache(i, factors)
at the same time. Then the thread A will create a OneValueCache
which overwrites the value created by threadB (OneValueChange is immutable but the reference to the variable cache can be changed).
It shows that the code is not thread safe.
Could anyone tell me why this piece of code is considered to be thread safe and why I am wrong ?