I was reading Java Concurrency In Practice, and saw a program like this:
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class TestImmutable {
private volatile OneValueCache cache =
new OneValueCache(new BigInteger("1"), new BigInteger[]{new BigInteger("1")});
public void service(int sec) {
Random random = new Random();
BigInteger i = new BigInteger(String.valueOf(random.nextInt()));
BigInteger[] factors = cache.getFactors(i, sec);
if (factors == null) {
factors = new BigInteger[]{i};
cache = new OneValueCache(i, factors);
System.out.println("After update: " + cache);
}
}
public static void main(String[] args) {
TestImmutable testImmutable = new TestImmutable();
new Thread(() -> testImmutable.service(0)).start();
new Thread(() -> testImmutable.service(15)).start();
}
}
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, int sec) {
try {
TimeUnit.SECONDS.sleep(sec);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Inside cache: " + this);
if (lastNumber == null || !lastNumber.equals(i))
return null;
else
return Arrays.copyOf(lastFactors, lastFactors.length);
}
@Override
public String toString() {
return "OneValueCache{" +
"lastNumber=" + lastNumber +
", lastFactors=" + Arrays.toString(lastFactors) +
'}';
}
}
It says in the book because class OneValueCache
is immutable, so its usage in TestImmutable
is thread safe.
However I'm curious about what happens if one thread gets suspended when in the middle of executing cache.getFactors(...)
and then another thread updates the cache
reference, then the first thread continues to execute the code. In this case which object is the first thread is working with, object before or after the second thread updated?
The code above was intended to create a scenario like this, but i can't control when and where the thread gets suspended, so I don't think its working right.
...invoking a method on a field...
You don't invoke methods on fields. You invoke methods on objects. When you write cache.getFactors(i,sec)
, that's invoking the getFactors
method on the object to which cache
refers. Let's call that object, O1, and let's call that invocation of the getFactors
method, Call1.
If some other thread subsequently sets cache=new OneValueCache(i,factors)
, then cache
now refers to a different object, O2. But, that doesn't change which object was the target of Call1. If that function invocation still is in progress, then it still is working on O1. When Call1 accesses lastFactors
or lastNumber
, it still is accessing the fields of the O1 object.
It's still true even when the other thread subsequently invokes cache.getFactors(i,sec)
. That's Call2 which is working on O2. But, Call1 doesn't know anything about that. It will continue to work on O1 until it returns.