Java synchronized 关键字是否刷新缓存? [英] Does Java synchronized keyword flush the cache?

查看:22
本文介绍了Java synchronized 关键字是否刷新缓存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

仅限 Java 5 及更高版本.假设有一台多处理器共享内存计算机(您现在可能正在使用一台).

Java 5 and above only. Assume a multiprocessor shared-memory computer (you're probably using one right now).

这里是单例延迟初始化的代码:

Here is a code for lazy initialization of a singleton:

public final class MySingleton {
  private static MySingleton instance = null;
  private MySingleton() { } 
  public static MySingleton getInstance() {
    if (instance == null) {
      synchronized (MySingleton.class) {
        if (instance == null) {
          instance = new MySingleton();
        }
      }
    }
    return instance;
  }
}

instance 是否必须声明为 volatile 以防止优化器重写 getInstance() 如下(这在顺序程序中是正确的):

Does instance have to be declared volatile in order to prevent the optimizer from rewriting getInstance() as follows (which would be correct in a sequential program):

public static MySingleton getInstance() {
  if (instance == null) {
    synchronized (MySingleton.class) {
      // instance must be null or we wouldn't be here  (WRONG!)
      instance = new MySingleton();
    }
  }
}

假设优化器不重写代码,如果 instance 没有声明 volatile 是否仍然保证在 synchronized 时刷新到内存> 块退出,进入同步块时从内存中读取?

Assuming the optimizer does not rewrite the code, if instance is not declared volatile is it still guaranteed to be flushed to memory when the synchronized block is exited, and read from memory when the synchronized block is entered?

我忘记将 getInstance() 设为静态.我认为这不会改变答案的有效性;你们都知道我的意思.

I forgot to make getInstance() static. I don't think that changes the validity of the answers; you all knew what I meant.

推荐答案

是的,instance 应该声明为 volatile.即便如此,建议不要使用双重检查锁定.它(或者准确地说是 Java 内存模型)曾经有一个严重的缺陷,它允许发布部分实现的对象.这已在 Java5 中修复,但 DCL 仍然是一个过时的习语,不再需要使用它 - 使用 懒惰初始化持有人习惯用语代替.

Yes, instance should be declared volatile. Even then, it is advised not to use double-checked locking. It (or to be precise, the Java Memory Model) used to have a serious flaw which permitted publication of partially implemented objects. This has been fixed in Java5, still DCL is an obsolete idiom and there is no need to use it anymore - use the lazy initialization holder idiom instead.

来自 Java 并发实践,第 16.2 节:

From Java Concurrency in Practice, section 16.2:

DCL 的真正问题是假设在不同步的情况下读取共享对象引用时可能发生的最糟糕的事情是错误地看到过时的值(在这种情况下,null);在这种情况下,DCL 习惯用法通过在持有锁的情况下再次尝试来补偿这种风险.但最坏的情况实际上要糟糕得多 - 可能会看到引用的当前值但对象状态的值已经过时,这意味着可能会看到对象处于无效或不正确的状态.

The real problem with DCL is the assumption that the worst thing that can happen when reading a shared object reference without synchronization is to erroneously see a stale value (in this case, null); in that case the DCL idiom compensates for this risk by trying again with the lock held. But the worst case is actually considerably worse - it is possible to see a current value of the reference but stale values for the object's state, meaning that the object could be seen to be in an invalid or incorrect state.

JMM(Java 5.0 及更高版本)中的后续更改使 DCL 能够工作如果 resource volatile,以及性能影响这是很小的,因为 volatile 读取通常只比非易失性读取稍微贵一点.然而,这是一个习语,其效用在很大程度上已经过去了 - 推动它的力量(缓慢的无竞争同步,缓慢的 JVM 启动)不再起作用,使其作为优化的效果降低.惰性初始化持有者习语提供了相同的好处并且更容易理解.

Subsequent changes in the JMM (Java 5.0 and later) have enabled DCL to work if resource is made volatile, and the performance impact of this is small since volatile reads are usually only slightly more expensive than nonvolatile reads. However, this is an idiom whose utility has largely passed - the forces that motivated it (slow uncontended synchronization, slow JVM startup) are no longer in play, making it less effective as an optimization. The lazy initialization holder idiom offers the same benefits and is easier to understand.

这篇关于Java synchronized 关键字是否刷新缓存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆