今天工作时,我在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.

其他回答

绝对是的。(不仅是Java, c#也是如此。)有时,您需要获取或设置一个值,该值保证是给定平台上的原子操作,例如int或boolean,但不需要线程锁定的开销。volatile关键字允许您确保在读取值时获得的是当前值,而不是在另一个线程上写入时被废弃的缓存值。

Volatile具有内存可见性的语义。基本上,volatile字段的值在写操作完成后对所有读取器(特别是其他线程)可见。如果没有volatile,读者可以看到一些未更新的值。

回答您的问题:是的,我使用一个volatile变量来控制某些代码是否继续循环。循环测试易变值,如果为真则继续。可以通过调用“stop”方法将条件设置为false。循环看到false,并在stop方法完成执行后测试该值时终止。

我强烈推荐的《Java并发实践》一书对volatile做了很好的解释。这本书的作者与问题中提到的IBM文章的作者是同一人(事实上,他在那篇文章的末尾引用了他的书)。我对volatile的使用被他的文章称为“模式1状态标志”。

如果您想了解更多关于volatile在底层是如何工作的,请阅读Java内存模型。如果你想超越这个层次,看看像Hennessy & Patterson这样的优秀计算机体系结构书籍,阅读关于缓存一致性和缓存一致性的内容。

用volatile关键字声明的变量有两个主要特性,这使得它很特殊。

如果我们有一个易失性变量,它不能被任何线程缓存到计算机的(微处理器)缓存内存中。访问总是发生在主存中。 如果对一个易失性变量正在进行写操作,并且突然请求了一个读操作,那么可以保证写操作将在读操作之前完成。

以上两个品质推断了这一点

所有读取volatile变量的线程肯定会读取最新的值。因为没有缓存值可以污染它。而且读请求只有在当前写操作完成后才会被授予。

另一方面,

如果我们进一步研究我提到的#2,我们可以看到volatile关键字是维护一个共享变量的理想方法,它有n个读线程,只有一个写线程可以访问它。一旦我们添加了volatile关键字,就完成了。没有任何线程安全方面的开销。

交谈,

我们不能仅仅使用volatile关键字来满足有多个写入线程访问它的共享变量。

下面是一个非常简单的代码,演示了volatile for variable的需求,它用于从其他线程控制线程执行(这是需要volatile的一个场景)。

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();

        Thread.sleep(500);
        long stoppedOn = System.nanoTime();

        task.stop(); // -----> do this to stop the thread

        System.out.println("Stopping on: " + stoppedOn);
    }
}

class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;

    public void stop() {
        state = false;
    } 

    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

当不使用volatile时:即使在“stop on: xxx”之后,你也永远不会看到“Stopped on: xxx”消息,并且程序继续运行。

Stopping on: 1895303906650500

当使用volatile时:你会立即看到'Stopped on: xxx'。

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

演示:https://repl.it/repls/SilverAgonizingObjectcode

如果您正在开发多线程应用程序,您将需要使用'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'。