博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程编程那些事:volatile解惑
阅读量:5825 次
发布时间:2019-06-18

本文共 2009 字,大约阅读时间需要 6 分钟。

1、 前言

\\

volatile关键字可能是Java开发人员“熟悉而又陌生”的一个关键字。本文将从volatile关键字的作用、开销和典型应用场景以及Java虚拟机对volatile关键字的实现这几个方面为读者全面深入剖析volatile关键字。

\\

volatile字面上有“挥发性的,不稳定的”意思,它是用于修饰可变共享变量(Mutable Shared Variable)的一个关键字。所谓“共享”是指一个变量能够被多个线程访问(包括读/写),所谓“可变”是指变量的值可以发生变化。换而言之,volatile关键字用于修饰多个线程并发访问的同一个变量,这些线程中至少有一个线程会更新这个变量的值。我们称volatile修饰的变量为volatile变量。我们知道锁的作用包括保障原子性、保障可见性以及保障有序性。volatile常被称为“轻量级锁”,其作用与锁有类似的地方——volatile也能够保障原子性(仅保障long/double型变量访问操作的原子性)、保障可见性以及保障有序性。

\\

本文所提及的“Java虚拟机”如无特别说明,均特指Oracle公司的HotSpot Java虚拟机。

\\

2. 保障long/double型变量访问操作的原子性

\\

不可分割的操作被称为原子操作(Atomic Operation)。所谓不可分割(Indivisible)是指一个操作从其执行线程以外的其他线程看来,该操作要么已经完成要么尚未开始,也就是说其他线程不会看到该操作的中间结果。如果一个操作是原子操作,那么我们就称该操作具有原子性(Atomicity)。

\\

Java语言规范(Java Language Specification,JLS)规定,Java语言中针对long/double型以外的任何变量(包括基础类型变量和引用型变量)进行的读、写操作都是原子操作,即Java语言规范本身并不规定针对long/double型变量进行读、写操作具有原子性。一个long/double型变量的读/写操作在32位Java虚拟机下可能会被分解为两个子步骤(比如先写低32位,再写高32位)来实现,这就导致一个线程对long/double型变量进行的写操作的中间结果可以被其他线程所观察到,即此时针对long/double型变量的访问操作不是原子操作。清单1所示的实验展示了这点。

\\

清单1 long/double型变量写操作的原子性问题Demo

\\
\/**\\* 本Demo必须使用32位Java虚拟机才能看到非原子操作的效果. \u0026lt;br\u0026gt;\\* 运行本Demo时也可以指定虚拟机参数“-client”\\*\\* @author Viscent Huang\\*/\\public class NonAtomicAssignmentDemo implements Runnable {\\static long value = 0;\\private final long valueToSet;\\public NonAtomicAssignmentDemo(long valueToSet) {\\this.valueToSet = valueToSet;\\}\\public static void main(String[] args) {\\// 线程updateThread1将data更新为0\\Thread updateThread1 = new Thread(new NonAtomicAssignmentDemo(0L));\\// 线程updateThread2将data更新为-1\\Thread updateThread2 = new Thread(new NonAtomicAssignmentDemo(-1L));\\updateThread1.start();\\updateThread2.start();\\// 不进行实际输出的OutputStream\\final DummyOutputStream dos = new DummyOutputStream();\\try (PrintStream dummyPrintSteam = new PrintStream(dos);) {\\// 共享变量value的快照(即瞬间值)\\long snapshot;\\while (0 == (snapshot = value) || -1 == snapshot) {\\// 不进行实际的输出,仅仅是为了阻止JIT编译器做循环不变表达式外提优化\\dummyPrintSteam.print(snapshot);\\}\\System.err.printf(\"Unexpected data: %d(0x%016x)\

转载地址:http://bqsdx.baihongyu.com/

你可能感兴趣的文章
Java并发框架——什么是AQS框架
查看>>
【数据库】
查看>>
Win配置Apache+mod_wsgi+django环境+域名
查看>>
linux清除文件内容
查看>>
WindowManager.LayoutParams 详解
查看>>
find的命令的使用和文件名的后缀
查看>>
Android的Aidl安装方法
查看>>
Linux中rc的含义
查看>>
曾鸣:区块链的春天还没有到来| 阿里内部干货
查看>>
如何通过Dataworks禁止MaxCompute 子账号跨Project访问
查看>>
js之无缝滚动
查看>>
Django 多表联合查询
查看>>
logging模块学习:basicConfig配置文件
查看>>
Golang 使用 Beego 与 Mgo 开发的示例程序
查看>>
+++++++子域授权与编译安装(一)
查看>>
asp.net怎样在URL中使用中文、空格、特殊字符
查看>>
路由器发布服务器
查看>>
实现跨交换机VLAN间的通信
查看>>
jquery中的data-icon和data-role
查看>>
python例子
查看>>