svobol13
svobol13

Reputation: 1960

Does synchronized block cause all write cache to flush?

I am interested in how synchronized works in sense how/when it flushes writes from local caches. Lets imagine I have following code:

class Scratch1 {
  int counter = 0;
  Scratch1() throws ExecutionException, InterruptedException {
    counter += 5;
    counter += 5;
    // Does this cause to flush possibly cached value written by main thread even if it locks
    // on totally unrelated object and the write doesnt happen inside the sync block?
    synchronized (String.class) {}
    Executors.newCachedThreadPool().submit(() -> {
      for (int i = 0; i < 1000; i++) {
        counter += 5;
      }
      synchronized (Integer.class) {}
    }).get();
    System.out.println(counter);
  }
}
class Scratch2 {
  int counter = 0;
  Scratch2() throws ExecutionException, InterruptedException {
    // Or is this only possible working way how flush written data.
    synchronized (String.class) {
      counter += 5;
      counter += 5;
    }
    Executors.newCachedThreadPool().submit(() -> {
      synchronized (Integer.class) {
        for (int i = 0; i < 1000; i++) {
          counter += 5;
        }
      }
    }).get();
    System.out.println(counter);
  }
}
class Scratch3 {
  volatile int counter = 0;
  Scratch3() throws ExecutionException, InterruptedException {
    counter += 5;
    counter += 5;
    Executors.newCachedThreadPool().submit(() -> {
      for (int i = 0; i < 1000; i++) {
        counter += 5;
      }
    }).get();
    System.out.println(counter);
  }
}

I have several questions:

  1. Does all three examples share same "thread-safety" level (taking into account specifics like first write is done by one thread and second write is done after first one (is it?) and by another thread) i.e. "is it guaranteed that 5010 is printed"?
  2. Is there performance difference (at least theoretical) in "operating" outside a synchronized block or working with non-volatile properties (I would expect volatile access to be slower as this post confirms) but in case of synchronized block is the "flushing" price paid only when crossing synchronized start/end or is there also difference while inside the block?

Upvotes: 1

Views: 610

Answers (1)

David Schwartz
David Schwartz

Reputation: 182883

I am interested in how synchronized works in sense how/when it flushes writes from local caches.

Actually, synchronized doesn't flush writes from local caches. It just acts as if it did so.

Does all three examples share same "thread-safety" level (taking into account specifics like first write is done by one thread and second write is done after first one (is it?) and by another thread) i.e. "is it guaranteed that 10 is printed"?

They all provide slightly different forms of thread safety. None of them are really safe if other threads are accessing the object at the same time. For example, another thread accessing counter would have to hold both the String.class and the Integer.class locks to ensure ensure it didn't see counter during an operation. The third one uses increment operations that aren't atomic, though it's safe if no other thread tries to modify counter.

Is there performance difference (at least theoretical) in "operating" outside a synchronized block or working with non-volatile properties (I would expect volatile access to be slower as this post confirms) but in case of synchronized block is the "flushing" price paid only when crossing synchronized start/end or is there also difference while inside the block?

No difference. Entering a synchronized block has a cost because the lock has to be acquired and some optimizations have to disabled across the entry point. Exiting the block has similar costs.

Inside the block, there are no costs because the safety is provided by the programmer ensuring they don't allow any thread to modify the object unless it holds the same lock and no two threads can hold the same lock at the same time. Generally speaking, code may not even know whether or not it's inside one or more synchronized blocks because it can be deep down the call tree.

Upvotes: 2

Related Questions