2
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);
  }

}

This is the code from the book Java concurrency in practice, my question is in this code specifically, we can remove the final keyword from the OneValueCache and still preserve the thread-safe, right, I am not sure why are these final keyword necessary.

Thanks.

liangwei guo
  • 51
  • 1
  • 5
  • Possible duplicate of [Safe publication through final](http://stackoverflow.com/questions/3974350/safe-publication-through-final) – user140547 Sep 29 '16 at 06:40
  • 1
    Yes, in theory one could neglect the `final` keywords, but you should not do so. You refer to Chapter 3.4.2 of Java Concurrency in Practice. As the paragraph says, we want to create a immutable object. Even though `lastNum` and `lastFactors` are private attributes, only the person(s) writing this class need to remember to not change them, it is better to declare them as `final`. For one, Java prevents you from re-assigning values to theses attributes. Furhtermore if someone reads this code, possibly with a comment saying "this class is immutable", it is easier to understand the code. – Turing85 Sep 29 '16 at 06:47

1 Answers1

0

It is not necessary in this very situation, but it is a bit complicated to reason about when done without the "final" keywords. Basically there are two concurrency problems we are trying to solve:

1) The visibility of the "cache" reference - solved by using "volatile" here.

2) State consistency (safe publication) of the OneValueCache object. As stated in the "Java Concurrency In Practice" book:

The publication requirements for an object depend on its mutability:

  • Immutable objects can be published through any mechanism;

  • Effectively immutable objects must be safely published;

...

So if you remove "final" usages from OneValueCache then you are making this class more of an effectively immutable class, at least from the visibility standpoint, because "final" has memory visibility semantics (somewhat similar to "volatile") under concurrency. So now instead of forgetting about object state consistency for any usages of the class you are forcing yourself to always think about safe publication when using it.

It also resembles what is described in chapter "16.1.4 Piggybacking on synchronization", because you would use the happens-before of writing/reading the volatile reference to guarantee that the OneValueCache object is in consistent state to all the threads after the construction. Basically it seems to be just a different explanation of the "safe publication" problem in this context.

Ruslan
  • 3,063
  • 1
  • 19
  • 28