在 Java 同步块中,写入是对所有字段可见,还是仅在同步变量上可见?

2022-09-03 13:55:13

假设您有此代码:

private String cachedToken;
private final Object lockObject = new Object();

....


retrieveToken(){
 synchronized(lockObject){
  if (cachedToken == null){
   cachedToken = goGetNewToken();
  }
  return cachedToken;
 }
}

写入操作是否对所有已锁定的线程可见?cachedTokenlockObject


答案 1

是的。在 lockObject 上同步可建立一个发生在关系之前(即设置内存屏障)。这意味着随后获得锁定的所有线程都将看到以前持有锁定时发生的任何更改。

但是,就其价值而言,您的惰性初始化实现存在缺陷。这是正确的方法:

private volatile String cachedToken;

retrieveToken() {
    if (cachedToken == null) {
        synchronized(lockObject) {
            if (cachedToken == null) {
                cachedToken = goGetNewToken();
            }
        }
    }
    return cachedToken
}

这样,当 Threads 首次开始请求锁时,您只需要获得少量锁。之后,cachedToken 将不会为 null,并且您不需要同步。


答案 2

当然,要确保两件事:synchronize

  • 原子数
  • 整个对象上的内存屏障(您在本例中期望的)

例如,确保内存屏障但不处理原子性。volatile


推荐