15

假设您有以下代码:

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

....


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

cachedToken所有已锁定的线程都可以看到写入lockObject吗?

4

3 回答 3

11

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

但是,对于它的价值,您对延迟初始化的实现是有缺陷的。这是正确的方法:

private volatile String cachedToken;

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

这样,当线程第一次开始请求它时,您只需要获得少量的锁。之后,cachedToken 将不会为空,并且您不需要同步。

于 2013-05-20T15:48:28.890 回答
8

当然,synchronize确保两件事:

  • 原子性
  • 整个对象上的内存屏障(您所期望的)

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

于 2013-05-20T15:51:28.110 回答
1

写入将在所有字段上可见。

从本质上讲,释放锁会强制从线程使用的工作内存中刷新所有写入,并获取锁会强制(重新)加载可访问字段的值。虽然锁定操作仅对同步方法或块中执行的操作提供排除,但这些内存效应被定义为覆盖执行操作的线程使用的所有字段

资料来源:Java 中的并发编程,Doug Lea http://gee.cs.oswego.edu/dl/cpj/jmm.html

于 2021-09-15T21:53:56.623 回答