(Java 高并发程序设计) 一篇文章带你深入了解 volatile 与 Java 内存模型(JMM)

已经学习了 Java 内存模型,知道了Java内存模型都是围绕着原子性、有序性和可见性展开的。为了在适当的场合,确保线程间的有序性、可见性和原子性。Java使用了一些特殊的操作或者关键字来声明、告诉虚拟机,在这个地方,要尤其注意,不能随意变动优化目标指令。关键字volatile就是其中之一。

当你用关键字volatile声明一个变量时,就等于告诉了虚拟机,这个变量极有可能会被某些程序或者线程修改。为了确保这个变量被修改后,应用程序范围内的所有线程都能够“看到”这个改动,虚拟机就必须采用一些特殊的手段,保证这个变量的可见性等特点

比如,根据编译器的优化规则,如果不使用关键字volatile声明变量,那么这个变量被修改后,其他线程可能并不会被通知到,甚至在别的线程中,看到变量的修改顺序都会是反的。一旦使用关键字volatile,虚拟机就会特别小心地处理这种情况。那这种情况,应该怎么处理才能保证每次写进去的数据不坏呢?最简单的一种方法就是加入关键字volatile声明,告诉编译器,这个long型数据,你要格外小心,因为它会不断地被修改。

下面的代码片段显示了关键字volatile的使用,限于篇幅,这里不再给出完整代码:

在这里插入图片描述
从这个案例中,我们可以看到,关键字volatile对于保证操作的原子性是有非常大的帮助的。但是需要注意的是,关键字volatile并不能代替锁,它也无法保证一些复合操作的原子性。比如下面的例子,通过关键字volatile是无法保证i++的原子性操作的。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此外,关键字volatile也能保证数据的可见性和有序性。下面再来看一个简单的例子:

在这里插入图片描述
上述代码中,ReaderThread线程只有在数据准备好时(ready为true),才会打印number的值。它通过ready变量判断是否应该打印。在主线程中,开启ReaderThread后,就为number和ready赋值,并期望ReaderThread能够看到这些变化并将数据输出。

在虚拟机的Client模式下,由于JIT并没有做足够的优化,在主线程修改ready变量的状态后,ReaderThread可以发现这个改动,并退出程序。但是在Server模式下,由于系统优化的结果,ReaderThread线程无法“看到”主线程中的修改,导致ReaderThread永远无法退出(因为代码第7行判断永远不会成立),这显然不是我们想看到的结果。这个问题就是一个典型的可见性问题。

注意:可以使用Java虚拟机参数-server切换到Server模式。

和原子性问题一样,我们只要简单地使用关键字volatile来声明ready变量,告诉Java虚拟机,这个变量可能会在不同的线程中修改。这样,就可以顺利解决这个问题了。

关于具体的 volatile 原理部分可以参考:Java 多线程中 volatile 的作用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值