JUC 整理笔记五之梳理 Varhandle(下)
前文综合描述了 Varhandle
以及 Varhandle
能够做的事情,但是要了解并使用 Varhandle
并非是一件容易的事。总的来说,要想很好地使用 Varhandle
,必须先了解plain(普通方式)、*opaque*、release/acquire、*volatile* 的区别及使用。
结合前面所学习的 jcstress ,本文用 jcsstress 作为并发测试工具来结合一些例子说明 plain、opaque、release/acqiure、volatile的特性。
如果不知道 jcstress 的使用的话,可以参考下 JUC整理笔记三之测试工具jcstress
内存可见性的区别
普通变量
上面测试案例是有一个 while 循环,然后另外一个线程修改 x 的值来达到让循环终止的目的,其测试结果 STALE、*TERMINATED* 并行存在, 说明在测试案例中,是有存在修改 x 值后,循环是没有结束的案例。
结果表明,多线程的环境中,普通变量是无法保证线程内存可见性的 。
opaque
这里使用了 Varhanle 来测试 opaque 的功能,测试的 Outcome 是不存在 STALE 。
结果表明,多线程环境中, opaque 是可以保存内存可见性的 。
release/acquire
该例子和 opaque 一样,也是不存在 STALE
结果表明,多线程环境下,release/acquire 是可以保证内存可见性的 。
volatile
这里的 volatile 是借助 Varhandle 来测试,用法和在变量前加 volatile 关键字是一样的。
和前面的两个例子一样,也是不存在 STALE
结果表明,多线程环境下 volatile 是可以保证内存可见性的 。
小结
普通变量是不保证内存可见性的,opaque 、 release/acquire 、volatile 是可以保证内存可见性。
特性区别
在测试之前,先准备下测试实体类
普通变量
上面的测试案例中,存在 “ 0, 0 ” 这个结果,说明代码中出现了重排序。
opaque
Varhandle 中对 getOpaque 、 setOpaque 的说明是,按程序顺序执行,不确保其他线程可见顺序。
测试一
结论: 结合前面 opaque 是保证可见性的结论,根据上面的例子可以得出,opaque不保证其它线程的可见顺序。
测试二
结论:上面两个例子都是没有 1, 0
这个例子,说明在 load
、store
情况下, opaque都是能够保证执行顺序的。
release/acquire
测试一
结论:不存在1, 0
这个结果,说明是 release/acquire 是可以按顺序执行的
测试二
结论:setRelease 确保前面的load和store不会被重排序到后面,但不确保后面的load和store重排序到前面;getAcquire 确保后面的load和store不会被重排序到前面,但不确保前面的load和store被重排序。
volatile
测试
结论: volatile 之间操作是能够确保顺序的,能保证变量之间的不被重排序
总结
前面的几个例子,说明了普通变量、opaque、release/acquire、volatile之间的区别
普通变量是不确保内存可见的,opaque、release/acquire、volatile是可以保证内存可见的
opaque 确保程序执行顺序,但不保证其它线程的可见顺序
release/acquire 保证程序执行顺序,setRelease 确保前面的load和store不会被重排序到后面,但不确保后面的load和store重排序到前面;getAcquire 确保后面的load和store不会被重排序到前面,但不确保前面的load和store被重排序。
volatile确保程序执行顺序,能保证变量之间的不被重排序。
代码地址:https://github.com/jfound/varhandle
版权声明: 本文为 InfoQ 作者【JFound】的原创文章。
原文链接:【http://xie.infoq.cn/article/b6e6986aa7afa4d8278ff6f63】。文章转载请联系作者。
评论