| 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. Java的内存模型要复杂一些。线程可以在本地内存中保存变量的值(例如,在机器寄存器中)。在这种情况下,当一个线程更改变量的值时,另一个线程可能看不到更改的变量。在由变量控制的循环中尤其如此(比如我们用来终止线程的done标志):循环线程可能已经将变量的值加载到寄存器中,并且不一定注意到另一个线程何时更改变量。(改变的确是主内存中的变量,导致两个变量数据不同步不一致) 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. 解决这个问题的一种方法是为变量提供setter和getter方法。然后,我们可以通过在这些方法上使用synchronized关键字来简单地同步访问。这是因为获取同步锁意味着寄存器中存储的所有临时值都被刷新到主存。然而,Java提供了一个更优雅的解决方案:volatile关键字。如果一个变量被标记为volatile,那么每次使用该变量时,都必须从主存中读取该变量。同样,每次写入变量时,值必须存储在主内存中。由于这些操作是原子操作,我们可以通过将完成标志标记为volatile来避免示例中的竞争条件。 |
volatile关键字的作用:
将变量声明成volatile时:声明了该变量仅仅存在于主内存中,得从主内存中读写该变量,寄存器不会单独存储该变量的,不允许用 voliatile声明的变量放在寄存器中,以避免变量在多线程共享的情况下出现,数据不一致的问题(重要知识点)
在JDK 1.2之前的大多数虚拟机版本中,Java内存模型的实际实现使得使用易失性变量成为一个没有实际意义的问题:变量总是从主存读取。在随后的Java迭代中,包括J2SE 5.0,虚拟机的实现变得更加复杂,并引入了新的内存模型和优化:这种趋势预计将在未来的Java版本中继续。在所有现代虚拟机实现中,开发人员不能假设变量将直接从主内存访问。
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.
那么,为什么有必要使用volatile呢?甚至有用? Volatile变量只能解决Java内存模型带来的问题。它们只能在使用变量的操作是原子操作时使用,这意味着访问变量的方法只能使用单个加载或存储。如果该方法有其他代码,则该代码可能不依赖于变量在其操作期间更改其值。例如,像递增和递减这样的操作(例如++和--)不能用于volatile 变量,因为这些操作是加载、更改和存储的语法糖。
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
正如我们提到的,我们可以通过使用同步setter和getter方法来访问变量来解决这个问题。然而,这将是相当复杂的。我们必须调用另一个方法,包括设置参数和返回变量。我们必须抓取并释放调用该方法所需的锁。这一切都是为了一行代码,一个原子操作,在一个循环中被多次调用。使用“完成”标志的概念非常常见,因此我们可以为volatile关键字提供非常有力的理由
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,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.
使用volatile 的要求似乎过于严格。它们真的很重要吗?这个问题可能导致无休止的辩论。目前,最好将volatile关键字视为一种强制虚拟机不要临时复制变量的方法。虽然我们可以同意,在许多情况下,您可能不会使用这些类型的变量,但它们是程序设计期间的一个选项。以后研究了限制性较小的类似变量(atomic variables原子变量):不仅是atomic variables原子变量,而且可以使用编程技术构建的变量。这使我们能够构建复杂的原子功能。
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
volatile如何处理数组?声明数组易失性会使数组引用本身volatile。数组中的元素不是volatile;虚拟机仍然可以在本地寄存器中存储单个元素的副本。无法指定数组的元素应被视为volatile型元素。因此,如果多个线程要访问数组元素,它们必须使用同步来保护数据。原子变量在这种情况下也有帮助



