双重检查锁定物品

发布于 2024-07-06 15:34:31 字数 721 浏览 6 评论 0原文

我正在阅读关于“双重检查锁定”的这篇文章在文章的主题之外,我想知道为什么在文章的某些时候作者使用了下一个习语:

清单 7. 尝试解决乱序写入问题

public static Singleton getInstance()   
  { 
      if (实例 == null) 
      { 
          同步(Singleton.class){//1 
              单例inst=实例;   //2 
              if (inst == null) 
              { 
                  同步(Singleton.class){//3 
                      inst = new Singleton();   //4 
                  } 
                  实例=实例;   //5 
              } 
          } 
      } 
      返回实例; 
  } 
  

我的问题是: 是否有任何理由使用同一个锁同步两次某些代码? 这有什么目的吗?

提前谢谢了。

I was reading this article about "Double-Checked locking" and out of the main topic of the article I was wondering why at some point of the article the author uses the next Idiom:

Listing 7. Attempting to solve the out-of-order write problem

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

And my question is:
Is there any reason to synchronize twice some code with the same lock?
Have this any purpose it?

Many thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

忘羡 2024-07-13 15:34:32

遵循 John Skeet 建议:

但是,要深入探讨这个主题
我推荐 Bill Pugh 的文章。 和
那就永远不要尝试:)

这是第二个同步块的关键:

这段代码构建了
内部的辅助对象
同步块。 直观的想法
这里应该有一个记忆
障碍点处
同步被释放,并且
应防止重新排序
Helper 对象的初始化
以及对该字段的分配
帮手。

因此,基本上,使用内部同步块,我们试图“欺骗”JMM 在同步块内创建实例,以强制 JMM 在同步块完成之前执行该分配。 但这里的问题是,JMM 正在引导我们,并将同步块之前的分配移到同步块内,将我们的问题移回到开头。

这是我从这些文章中了解到的,非常有趣,再次感谢您的回复。

Following the John Skeet Recommendation:

However, to go deeper into the subject
I'd recommend Bill Pugh's article. And
then never attempt it :)

And here is the key for the second sync block:

This code puts construction of the
Helper object inside an inner
synchronized block. The intuitive idea
here is that there should be a memory
barrier at the point where
synchronization is released, and that
should prevent the reordering of the
initialization of the Helper object
and the assignment to the field
helper.

So basically, with the Inner sync block, we are trying to "cheat" the JMM creating the Instance inside the sync block, to force the JMM to execute that allocation before the sync block finished. But the problem here is that the JMM is heading us up and is moving the assigment that is before the sync block inside the sync block, moving our problem back to the beginnig.

This is what i understood from those articles, really interesting and once more thanks for the replies.

心意如水 2024-07-13 15:34:32

好吧,但是文章说

由于内存模型的当前定义,清单 7 中的代码不起作用。 Java 语言规范 (JLS) 要求同步块内的代码不得移出同步块。 但是,它并没有说不在同步块中的代码不能移动到同步块中。

而且似乎 JVM 还对 ASM 中的“伪代码”进行了下一个翻译:

public static Singleton getInstance() 
  { 
    if (实例 == null) 
    { 
      同步(Singleton.class){//1 
        单例inst=实例;   //2 
        if (inst == null) 
        { 
          同步(Singleton.class){//3 
            //inst = new Singleton();   //4 
            实例=新单例();                
          } 
          //实例=实例;   //5 
        } 
      } 
    } 
    返回实例; 
  } 
  

到目前为止,“instance=inst”之后的不写入点还没有实现吗?

我现在将阅读这篇文章,感谢您的链接。

All right, but the article said that

The code in Listing 7 doesn't work because of the current definition of the memory model. The Java Language Specification (JLS) demands that code within a synchronized block not be moved out of a synchronized block. However, it does not say that code not in a synchronized block cannot be moved into a synchronized block.

And also seems like the JVM makes the next translation to "pseudo-code" in ASM:

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          //inst = new Singleton();      //4
          instance = new Singleton();               
        }
        //instance = inst;               //5
      }
    }
  }
  return instance;
}

So far, the point of no writes after the "instance=inst" is not accomplished?

I will read now the article, thanks for the link.

∞觅青森が 2024-07-13 15:34:32

从 Java 5 开始,您可以通过将字段声明为 volatile 来进行双重检查锁定。

请参阅 http://www.cs.umd.edu/~pugh /java/memoryModel/DoubleCheckedLocking.html 以获得完整的解释。

Since Java 5, you can make double-checked locking work by declaring the field volatile.

See http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html for a full explanation.

高跟鞋的旋律 2024-07-13 15:34:32

关于这个习惯用法,有一篇非常建议和澄清的文章:

http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1

另一方面,我认为 dhighwayman.myopenid 的意思是为什么作者将一个引用同一类 (synchronized(Singleton.class)) 的同步块放在引用同一类的另一个同步块中。 当在该块内创建一个新实例(Singleton inst = instance;)时,可能会发生这种情况,并且为了保证它是线程安全的,有必要编写另一个同步。

否则我看不出任何意义。

Regarding this idiom there is a very advisable and clarifying article:

http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1

On the other hand, I think what dhighwayman.myopenid means is why the writer has put one synchronized block referring to the same class (synchronized(Singleton.class)) within another synchronized block referring to the same class. It may happen as a new instance (Singleton inst = instance;) is created within that block and to guarantee it to be thread-safe it's necessary to write another synchronized.

Otherwise, I can't see any sense.

一桥轻雨一伞开 2024-07-13 15:34:32

请参阅有关 Java 内存模型的 Google 技术讲座,了解更精细的内容JMM 的要点。 由于这里缺少它,我还想指出 Jeremy Mansons 博客 'Java Concurrency' 特别是。 关于双重检查锁定的帖子(任何在Java 世界似乎有一篇关于此的文章:)。

See the Google Tech Talk on the Java Memory Model for a really nice introduction to the finer points of the JMM. Since it is missing here, I would also like to point out Jeremy Mansons blog 'Java Concurrency' esp. the post on Double Checked locking (anyone who is anything in the Java world seems to have an article on this :).

酷炫老祖宗 2024-07-13 15:34:32

对于 Java 5 及更高版本,实际上有一个双重检查变体,它比同步整个访问器更好。 双重检查锁定声明中也提到了这一点:

class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (helper == null)
                    helper = new Helper();
            }
        }
        return helper;
    }
}

这里的关键区别是在变量声明中使用易失性 - 否则它不起作用,并且无论如何它在Java 1.4或更低版本中不起作用。

For Java 5 and better there is actually a doublechecked variant that can be better than synchronizing the whole accessor. This is also mentioned in the Double-Checked Locking Declaration :

class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (helper == null)
                    helper = new Helper();
            }
        }
        return helper;
    }
}

The key difference here is the use of volatile in the variable declaration - otherwise it does not work, and it does not work in Java 1.4 or less, anyway.

不羁少年 2024-07-13 15:34:31

锁定两次的目的是尝试防止乱序写入。 内存模型指定了重新排序可以发生的位置,部分是通过锁来实现的。 该锁确保在“instance = inst;”之后不会出现任何写入操作(包括单例构造函数内的任何写入操作)。 线。

然而,为了更深入地探讨这个主题,我推荐 Bill Pugh 的文章。 然后永远不要尝试:)

The point of locking twice was to attempt to prevent out-of-order writes. The memory model specifies where reorderings can occur, partly in terms of locks. The lock ensures that no writes (including any within the singleton constructor) appear to happen after the "instance = inst;" line.

However, to go deeper into the subject I'd recommend Bill Pugh's article. And then never attempt it :)

知足的幸福 2024-07-13 15:34:31

本文指的是 5.0 之前的 Java 内存模型 (JMM)。 在该模型下,留下同步块会强制写入主内存。 因此,这似乎是为了确保 Singleton 对象在引用它之前被推出。 然而,它并不太有效,因为对实例的写入可以向上移动到块 - roach motel 中。

然而,5.0 之前的模型从未正确实现。 1.4应该遵循5.0模型。 类是惰性初始化的,所以你最好只写

public static final Singleton instance = new Singleton();

或者更好,不要使用单例,因为它们是邪恶的。

The article refers to the pre-5.0 Java memory model (JMM). Under that model leaving a synchronised block forced writes out to main memory. So it appears to be an attempt to make sure that the Singleton object is pushed out before the reference to it. However, it doesn't quite work because the write to instance can be moved up into the block - the roach motel.

However, the pre-5.0 model was never correctly implemented. 1.4 should follow the 5.0 model. Classes are initialised lazily, so you might as well just write

public static final Singleton instance = new Singleton();

Or better, don't use singletons for they are evil.

水晶透心 2024-07-13 15:34:31

Jon Skeet 是对的:请阅读 Bill Pugh 的 文章。 汉斯使用的习语是不起作用的精确形式,因此不应使用。

这是不安全的:

private static Singleton instance;

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

这也是不安全的:

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

永远不要做其中任何一个。

相反,同步整个方法:

    public static synchronized Singleton getInstance() {
      if (instance == null) {
        instance = new Singleton();
      }
      return instance;
    }

除非您每秒检索该对象无数次,否则实际上对性能的影响可以忽略不计。

Jon Skeet is right: read Bill Pugh's article. The idiom that Hans uses is the precise form that won't work, and should not be used.

This is unsafe:

private static Singleton instance;

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

This is also unsafe:

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

Don't do either of them, ever.

Instead, synchronise the whole method:

    public static synchronized Singleton getInstance() {
      if (instance == null) {
        instance = new Singleton();
      }
      return instance;
    }

Unless you're retrieving this object a zillion times a second the performance hit, in real terms, is negligible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文