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 ?
To put it simply, a class instance is immutable when its internal state can't be modified after it has been constructed. A MessageService object is effectively immutable since its state can't change after its construction. So, it's thread-safe.
Unlike synchronized methods or blocks, it does not make other threads wait while one thread is working on a critical section. Therefore, the volatile keyword does not provide thread safety when non-atomic operations or composite operations are performed on shared variables.
So, Immutable objects are always thread-safe, but their references may not be. To make their references thread-safe, we may need to access them from synchronized blocks/methods. @Akki No, ArrayList cannot be considered thread-safe unless we take care of the concurrency manually using things like synchronization.
The only real disadvantage of immutable classes is that they require a separate object for each distinct value. Creating these objects can be costly, especially if they are large.
So, two threads compute factors (one for 67, and the other for 89), and then store their result into the cache variable. The last thread that sets the variable wins. Let's say the first thread is the last one to store its factors in the cache. The cache thus contains the factors for 67.
If a subsequent execution asks for the factors of 67, it will get them from the cache (because the cache is not null, and contains the factors for 67). If it asks for the factors of another number, it won't get them from the cache, will thus compute the factors, and store them in the cache, hoping that the following requests will ask for the factors of the same number.
Nothing guarantees that two threads won't compute the factors from the same number. The only guarantee that this code offers is that, if the cache currently contains the factors for the requested number, these cached factors will be returned (and not factors for another number, or inconsistent data cause by a data race)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With