Why do I need synchronization?

I try to incorporate some shameful gaps in knowledge of Java threads, and I read Java Concurrency in practice from Brian Goetz et al. (BTW is highly recommended), and one of the earliest examples in the book leaves me with a question. In the following code, I fully understand why synchronization is required when updating member variables hitsand cacheHits, but why is this necessary for the method getHitswhen simply reading a variable hits?

Example code from chapter 2:

public class CachedFactorizer extends GenericServlet implements Servlet {
  private BigInteger lastNumber;
  private BigInteger[] lastFactors;
  private long hits;
  private long cacheHits;

public synchronized long getHits() {
    return hits;
}

public synchronized double getCacheHitRatio() {
    return (double) cacheHits / (double) hits;
}

public void service(ServletRequest req, ServletResponse resp) {
    BigInteger i = extractFromRequest(req);
    BigInteger[] factors = null;
    synchronized (this) {
        ++hits;
        if (i.equals(lastNumber)) {
            ++cacheHits;
            factors = lastFactors.clone();
        }
    }
    if (factors == null) {
        factors = factor(i);
        synchronized (this) {
            lastNumber = i;
            lastFactors = factors.clone();
        }
    }
    encodeIntoResponse(resp, factors);
}...

I have a feeling that this is due to atomicity, monitors and locks, but I don’t quite understand them, so please someone explain a little deeper?

Thanks in advance...

James

+3
6

. ( ), . "-" ( , , synchronized), .

, ++hits ++cacheHits service(). synchronized JVM , . , ++cacheHits ++hits cacheHits hits ( , ). , , :

Thread 1                  Thread 2
---------------           ----------------
++cacheHits (reordered)
  cacheHits=1, hits=0
                          read hits (as 0)
++hits
  cacheHits=1, hits=1
                          read cacheHits (as 1)

                          calculate 1 / 0 (= epic fail)

, , .

, . 1000 service(), cacheHits 500 hits 1. 50 000%, , .

, , , .

+1

, volatile , :

/ !

, 4 . , volatile .

+6

, , , . , , , , ...

+1

. , , .

: Java, , , . , , ​​ concurrency. , .

... Another option is to use AtomicLong, making it final. This variable should not be mutable, because the variable does not change, but only its contents, which is controlled by the AtomicLong class.

+1
source

If you are concerned about code performance (this is my guess), you should know that (1) stability is preferable to speed in parallel applications; and (2) other people like you at Sun, and thus developed this:

ReentrantReadWriteLock

0
source

Source: https://habr.com/ru/post/1736348/


All Articles