同步块是否会导致所有写缓存刷新?

Does synchronized block cause all write cache to flush?

我对 synchronized 的工作原理很感兴趣 how/when 它从本地缓存刷新写入。假设我有以下代码:

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

我有几个问题:

  1. 所有三个示例是否共享相同的 "thread-safety" 级别(考虑到诸如第一次写入由一个线程完成,第二次写入在第一个(是吗?)之后由另一个线程完成的细节)即 "is it guaranteed that 5010 is printed"?
  2. 在同步块之外或使用非易失性属性时 "operating" 是否存在性能差异(至少理论上)(我希望易失性访问速度比 this post confirms 慢)但以防万一synchronized 块的 "flushing" 价格仅在穿越 synchronized start/end 时支付,还是在块内也有差异?

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

实际上,synchronized 不会刷新本地缓存中的写入。它只是表现得好像它这样做了。

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"?

它们都提供略有不同的线程安全形式。 None 如果其他线程同时访问该对象,它们中的 None 是非常安全的。例如,访问 counter 的另一个线程必须同时持有 String.classInteger.class 锁,以确保它在操作期间不会看到 counter。第三个使用非原子的增量操作,但如果没有其他线程尝试修改 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?

没有区别。进入 synchronized 块是有代价的,因为必须获取锁并且必须在入口点禁用一些优化。退出区块的成本相似。

在块内部,没有任何成本,因为程序员提供了安全性,确保他们不允许任何线程修改对象,除非它持有相同的锁,并且没有两个线程可以同时持有相同的锁同时。一般来说,代码甚至可能不知道它是否在一个或多个 synchronized 块内,因为它可能位于调用树的深处。