Java中的volatile关键字

翻译自geeksforgeeks
使用volatile是保证线程安全的另一种方式(如同步,原子包装)。线程安全意味着多个线程可以同时使用方法或类实例而不会出现任何问题。
考虑下面的简单的例子:

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

假设两个线程正在使用SharedObj。如果两个线程在不同的处理器上运行,则每个线程可以拥有自己的sharedVariable本地副本。如果一个线程修改了它的值,则该更改可能不会立即反映在主存储器中的原始值中。这取决于缓存的写策略。现在另一个线程由于不知道这次修改,导致了数据不一致。
下图显示,如果两个线程在不同的处理器上运行,则sharedVariable的值在不同的线程中可能不同。

在这里插入图片描述
注意,在没有任何同步操作的情况下写入正常变量可能对任何读取线程都不可见(此行为称为顺序一致性)。虽然大多数现代硬件提供了良好的高速缓存一致性,因此很可能一个高速缓存中的修改会反映在其他高速缓存中,但依靠硬件来“修复”错误的应用程序并不是一个好的做法。

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;
}

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

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

  1. 相互排斥:这意味着一次只有一个线程或进程可以执行一段代码(临界区)。
  2. 可见性:这意味着一个线程对共享数据所做的更改对其他线程可见。

Java的synchronized关键字保证了互斥和可见性。如果我们使修改共享变量值的代码块同步,则只有一个线程可以进入该块,并且它所做的更改将反映在主内存中。同时刻所有其他尝试进入该块的线程将被阻止并进入休眠状态。
在某些情况下,我们可能只需要可见性而非原子性。在这种情况下使用synchronized是一种矫枉过正(overkill)并且可能导致可伸缩性问题。这时volatile关键字就派上用场了。volatile关键字具有synchronized的可见性但没有原子性。volatile变量的值永远不会被缓存,所有的写入和读取都将在主内存中完成。然而,volatile的使用仅局限于有限的一些情况,因为大多数时候需要原子性。比如,一个简单的递增语句,x = x + 1;或者x++,似乎是一个单独的操作,但实际上是必须以原子方式执行的复合的读取-修改-写入操作序列。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值