volatile关键字有什么用处?

805
今天在工作中,我遇到了Java中的volatile关键字。由于不是很熟悉它,我找到了这篇说明文章
鉴于该文章详细解释了关键字的相关内容,您是否曾经使用过它,或者是否可以想象出可以正确使用该关键字的情况?
25个回答

1
从Oracle文档page中可以了解到,需要使用volatile变量来解决内存一致性问题:

使用volatile变量可以降低内存一致性错误的风险,因为对于任何一个volatile变量的写入都会与随后对该变量的读取建立happens-before关系。

这意味着对于volatile变量的更改始终可见于其他线程。它还意味着当一个线程读取一个volatile变量时,它不仅能看到最新的volatile变化,还能看到导致变化的代码的副作用。
如Peter Parker所解释的那样,在没有volatile修饰符的情况下,每个线程的堆栈可能有自己的变量副本。通过将变量设置为volatile,已经解决了内存一致性问题。
请查看jenkov教程页面以获得更好的理解。
请查看相关SE问题以获取有关volatile和使用volatile的更多详细信息:

Java中volatile和synchronized的区别

一个实际应用场景:

你有许多线程,需要以特定格式打印当前时间,例如:java.text.SimpleDateFormat("HH-mm-ss")。你可以有一个类,将当前时间转换为SimpleDateFormat并每秒更新一次变量。所有其他线程可以简单地使用这个volatile变量来在日志文件中打印当前时间。


0

volatile变量基本上用于在更新后立即在主共享缓存行中进行更新(刷新),以便更改立即反映到所有工作线程中。


0

易变变量是轻量级同步。当所有线程之间的最新数据的可见性是必需的,而原子性可以被牺牲时,在这种情况下,应优先选择易变变量。由于易变变量既不缓存在寄存器中,也不缓存在其他处理器无法看到的缓存中,因此读取易变变量始终返回任何线程执行的最新写入。易变变量是无锁的。当场景符合上述标准时,我使用易变变量。


-1
以下是一个非常简单的代码示例,演示了在控制线程执行的变量被其他线程使用时(这是需要使用volatile的一种情况),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 时: 即使在 'Stopping on: xxx' 后,您永远不会看到 'Stopped on: xxx' 的消息,并且程序将继续运行。

Stopping on: 1895303906650500

使用 volatile 关键字:你会立即看到 'Stopped on: xxx'。

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

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

给那位点踩的人:能解释一下为什么要点踩吗?如果这不是真的,至少我会知道哪里出了问题。我已经添加了相同的评论两次,但不知道是谁一遍又一遍地删除。 - manikanta
同样的内容在这里也有解释:如何使用外部条件变量可靠地退出线程? - manikanta

-2

当使用变量时,volatile关键字可以确保读取该变量的线程看到相同的值。现在,如果有多个线程读取和写入一个变量,使变量成为volatile是不够的,数据将会被破坏。假设线程已经读取了相同的值,但每个线程都进行了一些更改(例如增加计数器),当写回内存时,数据完整性就会被破坏。这就是为什么需要使变量同步(有不同的方法)。

如果更改是由1个线程完成的,而其他线程只需要读取此值,则volatile是合适的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接