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

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


当前回答

假设一个线程修改了一个共享变量的值,如果你没有对该变量使用volatile修饰符的话。当其他线程想要读取这个变量的值时,它们看不到更新后的值,因为它们是从CPU的缓存而不是RAM内存中读取变量的值。这个问题也被称为能见度问题。

通过将共享变量声明为volatile,所有对计数器变量的写入都将立即写入主存。同样,所有对counter变量的读取都将直接从主存中读取。

public class SharedObject {
    public volatile int sharedVariable = 0;
}

对于非易失性变量,不能保证Java虚拟机(JVM)何时将数据从主存读取到CPU缓存,或何时将数据从CPU缓存写入主存。这可能会导致几个问题,我将在下面的部分中解释这些问题。


例子:

想象这样一种情况,两个或多个线程可以访问一个共享对象,该对象包含一个声明如下的计数器变量:

public class SharedObject {
    public int counter = 0;
}

再想象一下,只有线程1增加计数器变量,但是线程1和线程2都可以不时地读取计数器变量。

如果计数器变量没有被声明为volatile,则不能保证计数器变量的值何时从CPU缓存写入主存。这意味着CPU缓存中的计数器变量值可能与主存中的不相同。这种情况如下所示:

线程看不到一个变量的最新值,因为它还没有被另一个线程写回主存,这个问题被称为“可见性”问题。一个线程的更新对其他线程是不可见的。

其他回答

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

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布尔变量作为终止线程的标志。如果您已经启动了一个线程,并且希望能够安全地从另一个线程中断它,您可以让线程定期检查标志。要阻止它,将标志设置为true。通过将标志设置为易失性,可以确保正在检查它的线程在下次检查时看到它已经设置好,甚至不必使用同步块。

假设一个线程修改了一个共享变量的值,如果你没有对该变量使用volatile修饰符的话。当其他线程想要读取这个变量的值时,它们看不到更新后的值,因为它们是从CPU的缓存而不是RAM内存中读取变量的值。这个问题也被称为能见度问题。

通过将共享变量声明为volatile,所有对计数器变量的写入都将立即写入主存。同样,所有对counter变量的读取都将直接从主存中读取。

public class SharedObject {
    public volatile int sharedVariable = 0;
}

对于非易失性变量,不能保证Java虚拟机(JVM)何时将数据从主存读取到CPU缓存,或何时将数据从CPU缓存写入主存。这可能会导致几个问题,我将在下面的部分中解释这些问题。


例子:

想象这样一种情况,两个或多个线程可以访问一个共享对象,该对象包含一个声明如下的计数器变量:

public class SharedObject {
    public int counter = 0;
}

再想象一下,只有线程1增加计数器变量,但是线程1和线程2都可以不时地读取计数器变量。

如果计数器变量没有被声明为volatile,则不能保证计数器变量的值何时从CPU缓存写入主存。这意味着CPU缓存中的计数器变量值可能与主存中的不相同。这种情况如下所示:

线程看不到一个变量的最新值,因为它还没有被另一个线程写回主存,这个问题被称为“可见性”问题。一个线程的更新对其他线程是不可见的。

挥发性

volatile -> synchronized[关于]

Volatile表示对于程序员来说,该值总是最新的。问题是该值可以保存在不同类型的硬件内存中。例如,它可以是CPU寄存器,CPU缓存,RAM…СPU寄存器和CPU缓存属于CPU,不能共享数据,不像RAM在多线程环境中处于抢救状态

volatile关键字表示变量将直接从/写入RAM内存。它有一些计算足迹

Java 5通过支持happens-before扩展volatile[关于]

对volatile字段的写入发生在后续每次读取该字段之前。

Read is after write

volatile关键字不能修复竞态条件[关于],使用synchronized关键字[关于]

因此,只有当一个线程写入,而其他线程只是读取volatile值时才安全

是的,当你想要一个可变变量被多个线程访问时,必须使用volatile。这不是很常见的用例,因为通常你需要执行不止一个原子操作(例如,在修改变量之前检查变量状态),在这种情况下,你会使用同步块代替。