Why is the Synchronized block able to directly use the previous calculation result when two consecutive requests to factorize the same value?
code from 2-8 of Java Concurrency in Practice
public class two_8_CachedFactorizer implements Servlet {
@GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
@GuardedBy("this") private long hits;
@GuardedBy("this") private long cacheHits;
public synchronized long getHits(){return hits;}
public synchronized double getCacheHitRatio(){
return (double) cacheHits/(double)hits;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
BigInteger i=extractFromRequest(req);
BigInteger[] factors=null;
synchronized (this) {
++hits;
if (i.equals(lastNumber)) {
++cacheHits;
factors = lastFactors.clone();
}
}
if (factors==null)
{
factors=factor(i);//question in here :Suppose two identical requests arrive here at the same time
synchronized (this)
{
lastNumber=i;
lastFactors=factors.clone();
}
}
encodeIntoResponse(res,factors);
}
}
I believe you have at least a race condition on your hand. It could be that a earlier issued factor(i)
completes after a later issued factor(i)
; overwritting the value of the later issued factor(i)
. So the lastFactor and lastNumber could be updated to an earlier version. To resolve that problem I would add some checking in the last synchronized block.
In the current code, you can have multiple threads doing the same calculation. I'm not sure if that is desirable.