Java Threads - The volatile keyword
在网上看到很多关于 volatile 关键字的说明和使用问题, 今天引用Java Threads中的解释,感觉全面而详细,可惜是英文的.
这里很清晰的揭示了volatile 本身并不处理java读取数据的原子性问题,而是强制线程对数据的读写必须及时反映到主内存.
以下说明了volatile时, 其不能用于 long 和double 类型数据的根本和直接原因,其他很多解释都犯了概念性的错误.
其他原子类型数据的loading 和storing的原子性是由Java语言自身定义的,并不是由volatile来修饰实现的,这里很多网上的相关解释存在概念上的错误。
The Volatile Keyword
There is still one more threading issue in this example, and it has to do with the setDone() method. This method is called from the event-dispatching thread when the Stop button is pressed; it is called by an event handler (an actionPerformed() method) that is defined as an inner class to the SwingTypeTester class. The issue here is that this method is executed by the event-dispatching thread and changes data that is being used by another thread: the done flag, which is accessed by the thread of the AnimatedDisplayCanvas class.
So, can't we just synchronize the two methods, just as we did previously? Yes and no. Yes, Java's synchronized keyword allows this problem to be fixed. But no, the techniques that we have learned so far will not work. The reason has to do with the run() method. If we synchronized both the run() and setDone() methods, how would the setDone() method ever execute? The run( ) method does not exit until the done flag is set, but the done flag can't be set because the setDone() method can't execute until the run() method completes.
Definition: Scope of a LockThe scope of a lock is defined as the period of time between when the lock is grabbed and released. In our examples so far, we have used only synchronized methods; this means that the scope of these locks is the period of time it takes to execute the methods. This is referred to as method scope. Later in this chapter, we'll examine locks that apply to any block of code inside a method or that can be explicitly grabbed and released; these locks have a different scope. We'll examine this concept of scope as locks of various types are introduced. |
The problem at this point relates to the scope of the lock: the scope of the run() method is too large. By synchronizing the run() method, the lock is grabbed and never released. There is a way to shrink the scope of a lock by synchronizing only the portion of the run() method that protects the done flag (which we examine later in this chapter). However, there is a more elegant solution in this case.
The setDone() method performs only one operation with the done flag: it stores a value into the flag. The run() method also performs one operation with the done flag: it reads the value during each iteration of the loop. Furthermore, it does not matter if the value changes during the iteration of these methods, as each loop must complete anyway.
The issue here is that we potentially have a race condition because one piece of data is being shared between two different threads. In our first example, the race condition came about because the threads were accessing multiple pieces of data and there was no way to update all of them atomically without using the synchronized keyword. When only a single piece of data is involved, there is a different solution.
Java specifies that basic loading and storing of variables (except for long and double variables) is atomic. That means the value of the variable can't be found in an interim state during the store, nor can it be changed in the middle of loading the variable to a register. The setDone() method has only one store operation; therefore, it is atomic. The run( ) method has only one read operation. Since the rest of the run() method does not depend on the value of the variable remaining constant, the race condition should not exist in this case.
Unfortunately, Java's memory model is a bit more complex. Threads are allowed to hold the values of variables in local memory (e.g., in a machine register). In that case, when one thread changes the value of the variable, another thread may not see the changed variable. This is particularly true in loops that are controlled by a variable (like the done flag that we are using to terminate the thread): the looping thread may have already loaded the value of the variable into a register and does not necessarily notice when another thread changes the variable.
One way to solve this problem is to provide setter and getter methods for the variable. We can then simply synchronize access by using the synchronized keyword on these methods. This works because acquiring a synchronization lock means that all temporary values stored in registers are flushed to main memory. However, Java provides a more elegant solution: the volatile keyword. If a variable is marked as volatile, every time the variable is used it must be read from main memory. Similarly, every time the variable is written, the value must be stored in main memory. Since these operations are atomic, we can avoid the race condition in our example by marking our done flag as volatile.
In most releases of the virtual machine prior to JDK 1.2, the actual implementation of Java's memory model made using volatile variables a moot point: variables were always read from main memory. In subsequent iterations of Java, up to and including J2SE 5.0, implementations of virtual machines became more sophisticated and introduced new memory models and optimizations: this trend is expected to continue in future versions of Java. With all modern virtual machine implementations, developers can not assume that variables will be accessed directly from main memory.
So why is volatile necessary? Or even useful? Volatile variables solve only the problem introduced by Java's memory model. They can be used only when the operations that use the variable are atomic, meaning the methods that access the variable must use only a single load or store. If the method has other code, that code may not depend on the variable changing its value during its operation. For example, operations like increment and decrement (e.g., ++ and --) can't be used on a volatile variable because these operations are syntactic sugar for a load, change, and a store.
As we mentioned, we could have solved this problem by using synchronized setter and getter methods to access the variable. However, that would be fairly complex. We must invoke another method, including setting up parameters and the return variable. We must grab and release the lock necessary to invoke the method. And all for a single line of code, with one atomic operation, that is called many times within a loop. The concept of using a done flag is common enough that we can make a very strong case for the volatile keyword.
The requirements of using volatile variables seem overly restrictive. Are they really important? This question can lead to an unending debate. For now, it is better to think of the volatile keyword as a way to force the virtual machine not to make temporary copies of a variable. While we can agree that you might not use these types of variables in many cases, they are an option during program design. In Chapter 5, we examine similar variables (atomic variables) that are less restrictive: variables that are not only atomic but can be built on using programming techniques. This allows us to build complex atomic functionality.
How does volatile work with arrays? Declaring an array volatile makes the array reference itself volatile. The elements within the array are not volatile; the virtual machine may still store copies of individual elements in local registers. There is no way to specify that the elements of an array should be treated as volatile. Consequently, if multiple threads are going to access array elements, they must use synchronization in order to protect the data. Atomic variables can also help in this situation.
Java Threads - The volatile keyword的更多相关文章
- Java内存可见性volatile
概述 JMM规范指出,每一个线程都有自己的工作内存(working memory),当变量的值发生变化时,先更新自己的工作内存,然后再拷贝到主存(main memory),这样其他线程就能读取到更新后 ...
- java中的volatile关键字
java中的volatile关键字 一个变量被声明为volatile类型,表示这个变量可能随时被其他线程改变,所以不能把它cache到线程内存(如寄存器)中. 一般情况下volatile不能代替syn ...
- Java 并发 关键字volatile
Java 并发 关键字volatile @author ixenos volatile只是保证了共享变量的可见性,不保证同步操作的原子性 同步块 和 volatile 关键字机制 synchroniz ...
- Java并发编程 Volatile关键字解析
volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...
- java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)
概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...
- Java并发之volatile二
使用volatilekeyword的场景 Volatile 变量具有 synchronized 的可见性特性.可是不具备原子特性.这就是说线程可以自己主动发现 volatile 变量的最新值.Vola ...
- Java内存模型-volatile的内存语义
一 引言 听说在Java 5之前volatile关键字备受争议,所以本文也不讨论1.5版本之前的volatile.本文主要针对1.5后即JSR-133针对volatile做了强化后的了解. 二 vol ...
- Java 并发:volatile 关键字解析
摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...
- Java中的volatile关键字的功能
Java中的volatile关键字的功能 volatile是java中的一个类型修饰符.它是被设计用来修饰被不同线程访问和修改的变量.如果不加入volatile,基本上会导致这样的结果:要么无法编写多 ...
随机推荐
- 【iOS系列】- iOS吸附效果的实现 之 UICollectionView的使用全解
[iOS系列]- iOS吸附效果的实现 之 UICollectionView的使用全解 UICollectionView可以做很多的布局,在iOS开发中较为重要,所以这里就以实例来讲解UICollec ...
- 【iOS系列】- UITableView的使用技巧
[iOS系列]- UITableView的使用 UITableView的常用属性 indexpath.row:行 indexpath.section:组 separatorColor:分割线的颜色 s ...
- JAVA泛型类
泛型是JDK 5.0后出现新概念,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. 泛型类引入的好处不 ...
- GridView认识(一)
GridView认识(一)导读:一.显示数据 a.通过代码绑定显示数据 b.通过数据源控件绑定显示数据 二.外观控制 a.整体外观控制 b.列表行的控制 c.列表列的控制 内容:一.显示数据(一)代码 ...
- RDD的基本命令
1 创建RDD intRDD=sc.parallelize([3,1,2,5,6]) intRDD.collect()[4, 2, 3, 6, 7] 2 单RDD转换 (1) MAP def addo ...
- git 一次删除所有删除的文件
/*********************************************************************************** * git 一次删除所有删除的 ...
- 并不对劲的bzoj2638
为了反驳很对劲的太刀流,并不对劲的片手流决定与之针锋相对. 很对劲的太刀流-> 2638: 黑白染色 Time Limit: 20 Sec Memory Limit: 256 MBSubmit ...
- BZOJ4814,几何
对每个关键点i,将每个三角形缩成一个线段(因为三角形不相交),然后把线段两端点 和其他关键点一起 以i为中心点 极角排序. 扫一圈.扫到一个关键点j时, 判断当前最靠近i的线段是否遮盖i到j的路径, ...
- redis启动时指定配置文件
Redis 启动时指定配置文件需要通过 redis 服务启动才行: 安装服务的教程:http://blog.csdn.net/justinytsoft/article/details/54580919 ...
- 【转】jenkins上配置robotframeworkride自动化脚本任务
jenkins上配置robotframeworkride自动化脚本任务 编写好的自动化脚本,集成在jenkins上进行自动运行于监控,这里采用分布式构建,在一台slave上进行任务构建与自动化脚本的运 ...