Java中的volatile关键字

使用volatile是使类线程安全的另一种方法(如同步的原子包装器)。线程安全意味着一个方法或类实例可以被多个线程同时使用而不会出现任何问题。考虑下面的例子。

null
class SharedObj{   // Changes made to sharedVar in one thread   // may not immediately reflect in other thread   static int sharedVar = 6;}

假设有两个线程正在工作 SharedObj 。如果两个线程在不同的处理器上运行,则每个线程可能都有自己的本地副本 共享变量 。如果一个线程修改了它的值,那么更改可能不会立即反映在主内存中的原始线程中。这取决于缓存的写入策略。现在,另一个线程不知道修改后的值,这会导致数据不一致。

下图显示,如果两个线程在不同的处理器上运行,则 共享变量 在不同的线程中可能会有所不同。

volatileJava

请注意,在没有任何同步操作的情况下写入普通变量,可能对任何读取线程都不可见(这种行为称为 顺序一致性 )。尽管大多数现代硬件提供了良好的缓存一致性,因此,一个缓存中的更改很可能会反映在另一个缓存中,但依靠硬件“修复”故障应用程序并不是一种好做法。

class SharedObj{   // volatile keyword here makes sure that   // the changes made in one thread are    // immediately reflect in other thread   static volatile int sharedVar = 6;}

请注意,volatile不应与static修饰符混淆。静态变量是在所有对象之间共享的类成员。主内存中只有一个副本。

volatile vs synchronized: 在继续之前,我们先来看一下锁和同步的两个重要特性。

  1. 互斥: 这意味着一次只能有一个线程或进程执行一段代码(关键部分)。
  2. 可见度 :这意味着一个线程对共享数据所做的更改对其他线程可见。

Java的synchronized关键字保证了互斥性和可见性。如果我们使修改共享变量值的线程块同步,只有一个线程可以进入该块,它所做的更改将反映在主内存中。试图同时进入该块的所有其他线程都将被阻止并进入睡眠状态。

在某些情况下,我们可能只想要可见性,而不是原子性。在这种情况下,使用synchronized是过分的,可能会导致可伸缩性问题。这里是救援的地方。Volatile变量具有synchronized的可见性特征,但不具有原子性特征。volatile变量的值永远不会被缓存,所有的写操作和读操作都会在主内存中进行。然而,由于大多数情况下需要原子性,volatile的使用仅限于一组非常有限的情况。例如,一个简单的递增语句,比如x=x+1;或者x++似乎是一个单一的操作,但实际上是一个复合的读-修改-写操作序列,必须以原子方式执行。

JAVA

// Java Program to demonstrate the
// use of Volatile Keyword in Java
public class VolatileTest {
private static final Logger LOGGER
= MyLoggerFactory.getSimplestLogger();
private static volatile int MY_INT = 0 ;
public static void main(String[] args)
{
new ChangeListener().start();
new ChangeMaker().start();
}
static class ChangeListener extends Thread {
@Override public void run()
{
int local_value = MY_INT;
while (local_value < 5 ) {
if (local_value != MY_INT) {
LOGGER.log(
Level.INFO,
"Got Change for MY_INT : {0}" ,
MY_INT);
local_value = MY_INT;
}
}
}
}
static class ChangeMaker extends Thread {
@Override public void run()
{
int local_value = MY_INT;
while (MY_INT < 5 ) {
LOGGER.log(Level.INFO,
"Incrementing MY_INT to {0}" ,
local_value + 1 );
MY_INT = ++local_value;
try {
Thread.sleep( 500 );
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}


输出(使用Volatile关键字):

Incrementing MY_INT to 1Got Change for MY_INT : 1Incrementing MY_INT to 2Got Change for MY_INT : 2Incrementing MY_INT to 3Got Change for MY_INT : 3Incrementing MY_INT to 4Got Change for MY_INT : 4Incrementing MY_INT to 5Got Change for MY_INT : 5

输出(不带Volatile关键字)

Incrementing MY_INT to 1Incrementing MY_INT to 2Incrementing MY_INT to 3Incrementing MY_INT to 4Incrementing MY_INT to 5

Java与C/C++中的volatile:

Java中的Volatile与 C/C中的“volatile”限定符++ .对于Java,“volatile”告诉编译器,变量的值永远不能被缓存,因为它的值可能会在程序本身的范围之外发生变化。在C/C++中,在开发嵌入式系统或设备驱动程序时,需要使用“volatile”,您需要读取或写入内存映射的硬件设备。特定设备寄存器的内容可能随时发生变化,因此需要使用“volatile”关键字来确保编译器不会优化此类访问。

本文由 苏拉布·库马尔 。如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请发表评论

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享