今天工作时,我在Java中遇到了volatile关键字。不是很熟悉,我找到了这个解释。

鉴于那篇文章详细解释了所讨论的关键字,您曾经使用过它吗?或者您曾经看到过以正确方式使用该关键字的情况吗?


当前回答

在我看来,除了停止线程之外,使用volatile关键字的两个重要场景是:

Double-checked locking mechanism. Used often in Singleton design pattern. In this the singleton object needs to be declared volatile. Spurious Wakeups. Thread may sometimes wake up from wait call even if no notify call has been issued. This behavior is called spurious wakeup. This can be countered by using a conditional variable (boolean flag). Put the wait() call in a while loop as long as the flag is true. So if thread wakes up from wait call due to any reasons other than Notify/NotifyAll then it encounters flag is still true and hence calls wait again. Prior to calling notify set this flag to true. In this case the boolean flag is declared as volatile.

其他回答

是的,我经常使用它——它对多线程代码非常有用。你指的那篇文章很好。不过有两件重要的事情要记住:

You should only use volatile if you completely understand what it does and how it differs to synchronized. In many situations volatile appears, on the surface, to be a simpler more performant alternative to synchronized, when often a better understanding of volatile would make clear that synchronized is the only option that would work. volatile doesn't actually work in a lot of older JVMs, although synchronized does. I remember seeing a document that referenced the various levels of support in different JVMs but unfortunately I can't find it now. Definitely look into it if you're using Java pre 1.5 or if you don't have control over the JVMs that your program will be running on.

…volatile修饰符保证任何读取字段的线程都能看到最近写入的值。——乔希·布洛赫 如果您正在考虑使用volatile,请仔细阅读java.util.concurrent包,它处理原子行为。 维基百科上关于单例模式的帖子显示了volatile的使用。

Volatile执行以下操作。

不同线程对volatile变量的读写总是从内存,而不是从线程自己的缓存或cpu寄存器。所以每个线程总是处理最新的值。 2>当两个不同的线程在堆中使用相同的实例或静态变量时,其中一个线程可能会认为其他线程的操作是无序的。请看jeremy manson的博客。但不稳定在这里有所帮助。

下面完全运行的代码展示了如何在不使用synchronized关键字的情况下以预定义的顺序执行多个线程并打印输出。

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

为了实现这一点,我们可以使用以下完整的运行代码。

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

下面的github链接有一个自述,它给出了适当的解释。 https://github.com/sankar4git/volatile_thread_ordering

如果您正在开发多线程应用程序,您将需要使用'volatile'关键字或'synchronized'以及您可能拥有的任何其他并发控制工具和技术。桌面应用就是这样一个例子。

If you are developing an application that would be deployed to application server (Tomcat, JBoss AS, Glassfish, etc) you don't have to handle concurrency control yourself as it already addressed by the application server. In fact, if I remembered correctly the Java EE standard prohibit any concurrency control in servlets and EJBs, since it is part of the 'infrastructure' layer which you supposed to be freed from handling it. You only do concurrency control in such app if you're implementing singleton objects. This even already addressed if you knit your components using frameworkd like Spring.

因此,在Java开发的大多数情况下,应用程序是一个web应用程序,并使用IoC框架,如Spring或EJB,你不需要使用'volatile'。

The volatile key when used with a variable, will make sure that threads reading this variable will see the same value . Now if you have multiple threads reading and writing to a variable, making the variable volatile will not be enough and data will be corrupted . Image threads have read the same value but each one has done some chages (say incremented a counter) , when writing back to the memory, data integrity is violated . That is why it is necessary to make the varible synchronized (diffrent ways are possible)

如果修改是由一个线程完成的,而其他线程只需要读取这个值,则volatile将是合适的。