简体   繁体   English

为什么将 volatile 与同步块一起使用?

[英]why using volatile with synchronized block?

I saw some examples in java where they do synchronization on a block of code to change some variable while that variable was declared volatile originally .. I saw that in an example of singleton class where they declared the unique instance as volatile and they sychronized the block that initializes that instance ... My question is why we declare it volatile while we synch on it, why we need to do both??我在 java 中看到了一些示例,它们在代码块上进行同步以更改某些变量,而该变量最初被声明为 volatile。初始化那个实例......我的问题是为什么我们在同步它时声明它是可变的,为什么我们需要同时做? isn't one of them is sufficient for the other ??其中一个对另一个不够用吗??

public class SomeClass {
    volatile static Object uniqueInstance = null;

    public static Object getInstance() {
        if (uniqueInstance == null) {
            synchronized (someClass.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new SomeClass();
                }
            }
        }
        return uniqueInstance;
    }
}

thanks in advance.提前致谢。

Synchronization by itself would be enough in this case if the first check was within synchronized block (but it's not and one thread might not see changes performed by another if the variable were not volatile).在这种情况下,如果第一次检查在同步块内,那么同步本身就足够了(但事实并非如此,如果变量不是易失性的,一个线程可能看不到另一个线程执行的更改)。 Volatile alone would not be enough because you need to perform more than one operation atomically.仅使用 Volatile 是不够的,因为您需要以原子方式执行多个操作。 But beware!但要小心! What you have here is so-called double-checked locking - a common idiom, which unfortunately does not work reliably .您在这里拥有的是所谓的双重检查锁定 - 一种常见的习惯用法,不幸的是它不能可靠地工作 I think this has changed since Java 1.6, but still this kind of code may be risky.我认为自 Java 1.6 以来这已经发生了变化,但这种代码仍然可能存在风险。

EDIT : when the variable is volatile, this code works correctly since JDK 5 (not 6 as I wrote earlier), but it will not work as expected under JDK 1.4 or earlier.编辑:当变量是 volatile 时,此代码从 JDK 5(不是我之前写的 6)开始正常工作,但在 JDK 1.4 或更早版本下将无法正常工作。

This uses the double checked locking, note that the if(uniqueInstance == null) is not within the synchronized part.这使用了双重检查锁定,请注意if(uniqueInstance == null)不在同步部分内。

If uniqueInstance is not volatile, it might be "initialized" with a partially constructed object where parts of it isn't visible to other than the thread executing in the synchronized block.如果uniqueInstance不是易失性的,则它可能会使用部分构造的对象“初始化”,其中除了在synchronized块中执行的线程之外,它的一部分是不可见的。 volatile makes this an all or nothing operation in this case.在这种情况下, volatile 使其成为全有或全无的操作。

If you didn't have the synchronized block, you could end up with 2 threads getting to this point at the same time.如果您没有同步块,则最终可能有 2 个线程同时到达这一点。

if(uniqueInstance == null) {
      uniqueInstance = new someClass(); <---- here

And you construct 2 SomeClass objects, which defeats the purpose.并且您构造了 2 个 SomeClass 对象,这违背了目的。

Strictly speaking, you don't need volatile , the method could have been严格来说,你不需要 volatile ,方法本来可以

public static someClass getInstance() {
    synchronized(FullDictionary.class) {
         if(uniqueInstance == null) {
             uniqueInstance = new someClass();
          }
         return uniqueInstance;
    }
}

But that incurs the synchronization and serialization of every thread that performs getInstance().但这会导致执行 getInstance() 的每个线程的同步和序列化。

This post explains the idea behind volatile. 这篇文章解释了 volatile 背后的想法。

It is also addressed in the seminal work, Java Concurrency in Practice .它也在开创性工作Java Concurrency in Practice 中得到解决

The main idea is that concurrency not only involves protection of shared state but also the visibility of that state between threads: this is where volatile comes in. (This larger contract is defined by the Java Memory Model .)主要思想是并发不仅涉及共享状态的保护,还涉及线程之间该状态的可见性:这就是 volatile 的用武之地。(这个更大的契约由Java 内存模型定义。)

You can do synchronization without using synchronized block.您可以在不使用同步块的情况下进行同步。 It's not a necessary to use volatile variable in it... volatile updates the one variable from main memory..and synchronized Update all shared variables that have been accessed from main memory.. So you can use it according to your requirement..没有必要在其中使用 volatile 变量...... volatile 从主内存更新一个变量..和同步更新从主内存访问的所有共享变量..所以你可以根据你的要求使用它..

My two cents here我的两分钱在这里

Frist a quick explanation of the intuition of this code首先快速解释一下这段代码的直觉

if(uniqueInstance == null) {
        synchronized(someClass.class) {
            if(uniqueInstance == null) {
                uniqueInstance = new someClass();
            }
        }
    }

The reason it checks uniqueInstance == null twice is to reduce the overhead of calling the synchronized block which is relatively slower.它检查 uniqueInstance == null 两次的原因是为了减少调用相对较慢的 synchronized 块的开销。 So called double-checked locking.所谓的双重检查锁定。

Second, the reason it uses synchronized is easy to understand, it make the two operations inside the synchronized block atomic.其次,它使用synchronized的原因很容易理解,它使synchronized块内部的两个操作原子化。

Last the volatile modifier makes sure all threads see the same copy so the very first check outside of the synchronized block will see the value of uniqueInstance in a way which is "synchronized" with the synchronized block.最后, volatile 修饰符确保所有线程看到相同的副本,因此同步块之外的第一个检查将以与同步块“同步”的方式看到 uniqueInstance 的值。 Without the volatile modifier one thread can assign a value to uniqueInstance but the other thread may not see it by the first check.如果没有 volatile 修饰符,一个线程可以为 uniqueInstance 分配一个值,但另一个线程在第一次检查时可能看不到它。 (Although the second check will see it) (虽然第二次检查会看到)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM