我对synchronized
的工作方式(从本地缓存中清除写入的方式/时间)很感兴趣。假设我有以下代码:
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);
}
}
我有几个问题:
答案 0 :(得分:2)
我对同步如何在何时/何时从本地缓存中清除写入的意义感兴趣。
实际上,synchronized
不会从本地缓存中清除写入。就像它这样做一样。
这三个示例是否共享相同的“线程安全性”级别(考虑到细节,例如第一次写入由一个线程完成,第二次写入在第一个(是吗?)之后并由另一个线程完成),即“是保证打印10张?”
它们都提供稍微不同形式的线程安全性。如果其他线程同时访问该对象,则它们都不是真正安全的。例如,另一个访问counter
的线程将必须同时持有String.class
和Integer.class
锁,以确保在操作过程中没有看到counter
。尽管没有其他线程尝试修改counter
是安全的,但是第三个方法使用的不是原子的增量操作。
在同步块外进行“操作”或使用非易失性属性是否存在性能差异(至少是理论上的差异)(如本文所证实,我希望易失性访问速度较慢),但在同步块的情况下,“仅在跨越同步的开始/结束时才支付“冲洗”价格,或者在区块内部时是否也有差异?
没有区别。输入synchronized
块是有成本的,因为必须获取锁并且必须在整个入口点禁用某些优化。退出街区的费用相似。
在该块内没有任何费用,因为程序员提供了安全性,确保他们不允许任何线程修改该对象,除非该对象拥有相同的锁并且没有两个线程可以同时拥有相同的锁。一般来说,代码甚至可能不知道它是否在一个或多个synchronized
块中,因为它可以深入到调用树中。