介绍
在之前的文章《
Java并发编程~变量可见性和volatile
》中,我们提到在多线程场景下,volatile可以保证变量的可见性。之前的变量是值类型,如果变量是对象引用类型,这种可见性还能保证吗?实际上,对象引用本身的可见性还是保证的,但是对于对象中的字段值,可见性是不能保证的。
我们看如下案例。
对象引用的可见性问题
在上面的代码中,我们定义了一个值对象 ,内部包含两个字段 和 。我们在主程序中定义一个 ,即引用 本身是volatile的。
我们启动两个线程,写线程创建新的 对象 ,程序逻辑是保证 和 相等;读线程则读取 中的 和 的值,读取后进行比对,如果 和 不相等,则输出不相等的情况。
我们预期读线程读到的 和 值始终相等,但是如果我们将程序循环运行足够多次,比如2万次,偶尔我们会得到 和 不相等的情况,也就说虽然 引用本身是volatile的,但是vo内部的字段值可能会出现对其它线程的可见性和不一致情况。
样例输出:
注意这个输出不确定,取决于计算机配置,你可能需要多跑几次才能遇到不一致情况。
如何解决上述问题,有两种方法,下面给出案例代码。
方法一:拷贝字段值
上述代码运行再多次也不会出现不一致情况,区别在于 方法拷贝了字段的值,可以避免不一致问题。
方法二:使用原子引用类型AtomicReference
上述代码运行再多次也不会出现不一致情况, 可以保证原子引用对象中的字段值对读线程始终可见,避免一致性问题。
结论
对于引用类型使用volatile,可以保证引用类型变量本身的可见性,但是其内部的字段值还是可能存在可见性问题。
解决上述问题的一种办法是拷贝值,另外一种办法是使用原子引用类型AtomicReference。
本文代码已经上传github,下载请参考附录。
附录
参考代码:https://github.com/spring2go/tutorials/tree/master/core-java-concurrency
领取专属 10元无门槛券
私享最新 技术干货