?? Volatile is a familiar keyword in Java concurrency programming. Even if you have never used it, you will occasionally see it in technical books or blogs. The explanation of the volatile keyword is often taken over: the modified variable has visibility, but it does not guarantee atomicity. But exactly how to ensure visibility, what visibility is ... Such problems, when confronted with this condensed explanation, create a confusion that knows what it is. Then I will try to penetrate the meaning of the volatile keyword thoroughly in this article.
-
Visibility
?? Visibility, as the name implies, the degree of visibility. If you open the flashlight in the bed, only you know that the flashlight is turned on, then the visibility is very poor, but you put a camera in the bed, open the flashlight, in the news broadcast in the process of opening the camera, this visibility is very high.
?? In the case of CPU serial computing, completely dictatorship the program's memory and calculation, any changes are completely controlled by him. This is not the case in concurrent programming, where multiple CPUs are fragmented and, for shared variables, can be seen as outdated data, the results of which can be overwritten immediately, causing serious confusion. Take a look at the code
public class Testvolatile implements runnable{//self-increment variable ipublic int i = 0; @Overridepublic void Run ( {while (true) {i++;//constant self-increment}}public static void main (string[] args) throws Interruptedexception {Testv Olatile TV = new Testvolatile (); Seesome ss = new Seesome (); SS.V = TV; thread T1 = new thread (TV); Thread t2 = new Thread (ss); T1.start (); T2.start (); Thread.Sleep (10); Print I and S System.out.println ("tv.i =" + tv.i + "-------------ss.s =" + SS.S); System.exit (0);}} Class Seesome implements Runnable{public testvolatile v;//key member variable spublic int s; @Overridepublic void Run () {while (true {s = v.i;//constantly assigns the value of V.I to S}}}
?? This is the result of the output:
tv.i = 4048335-------------ss.s = 547818Process finished with exit code 0
?? The general explanation of this program, the Testvolatile class in an INT member variable i, the Run method is derived from the increment i. The Seesome class has two member variables, one is Testvolatile, the other is an int variable s, and the Run method is used to assign the member Testvolatile I to s. That is, starting two threads, testvolatile with the member Ifrom the increment, and seesome is used to assign the I in testvolatile to its member variable s. It is clear from the results that two values are far from the other.
?? Look at a program, and the rest of you don't have to change. Change only I in Testvolatile to be modified by volatile
public class TestVolatile implements Runnable{ volatile int i = 0; @Override public void run() { while (true){ i++; } }
?? And look at the output:
tv.i = 362312-------------ss.s = 362790Process finished with exit code 0
?? This time is very close, this is the power of volatile, in fact, although the output statement in a row, but the output is the first to take the value of I, after i take the value of I,I also in the thread self-increment, so see s Value is larger than the value of I. See here you broadly on the volatile keyword some sense of understanding, with the text vaguely summed up: multi-threading, it seems to be able to get the latest volatile value. At its root, how does volatile guarantee its visibility, if deeper, may be understood more thoroughly.
?? Let's look at a piece of code and convert the self-increment of the volatile modified integer I into a JVM directive, such as:
mov 0xc(%r10),%r8d ; Loadinc %r8d ; Incrementmov %r8d,0xc(%r10) ; Storelock //这里这里 addl $0x0,(%rsp) ; StoreLoad Barrier
?? Do not worry too much to understand, you just need to focus on the word lock is good. The following is the explanation of lock (if you feel that a mouthful can be skipped, excerpt from "IA-32 Vol. 3: System Programming Guide")
When modifying a memory operation, the lock prefix invokes a locked read-modify-write operation (atomic). This mechanism is used in multi-processor systems for reliable communication between processors. In Pentium and earlier IA-32 processors, the lock prefix would allow the processor to detect lock# signals when executing instructions that always cause an explicit bus lock to occur. In Pentium 4, Intel Xeon and P6 series processors, the lock operation is handled by a cache lock or bus lock. If memory access is cacheable, and only a single cache line is affected, the cache lock is invoked, and the real memory location in memory in the system bus and system is not locked in the operation. Here, the Pentium 4, Intel Xeon, or P6 series processors on the other buses write back all the modified data and invalidate their caches to ensure system memory consistency. If memory access cannot be cached and/or it crosses the boundary of the cache line, the lock# signal of the processor is checked and the processor does not respond to bus control requests during lockout. The RSM (returned from SMM) directive restores the state of the processor (from the context) to the System Management mode (SMM) before the interrupt.
?? In fact, in general, Lock did two things, one is to write the current processor cache data back into memory, and the other is to write back to the memory operation will cause the other CPU cache the address of the data invalid. For example, there are three people, Xiao Ming, small black, small white in different classrooms, there is an LED display on the playground, the number of LED display is 10, we all put this number in their notebooks. Xiao Ming intends to change the number, he changed the number of the notebook to 11, in the process of synchronizing to the LED display board, he shouted at the small black and small white, your numbers are invalid, so small black and small white to the notebook on the number crossed off, if they need to change the LED number they will go to the LED display board to look at. If you can find memory, CPU cache, and CPU-related things in the above story and can correspond to the two things that lock does, you should also understand the principle of lock and volatile visibility. The question about the visibility of volatile is here, and then we'll go on to find out how volatile prevents reordering.
Re-order
?? Reordering is actually a very simple question, while we are writing the code in a row of lines, the results of the compilation in bytecode are in order, but after the CPU or JVM optimization, in the concurrent program may bring unexpected surprises.
?? Singleton mode Everyone should be more clear, let's look at a double detection lock single-case mode.
public class Singleton {//单例对象private static Singleton instance = null;//私有化构造器,避免外部通过构造器构造对象private Singleton(){}//这是静态工厂方法,用来产生对象public static Singleton getInstance(){ if(instance ==null){ //同步锁防止多次new对象 synchronized (Singleton.class){ //锁内非空判断也是为了防止创建多个对象 if(instance == null){ instance = new Singleton(); } } } return instance;}}
?? This seemingly tedious single-case approach hides an uncontrollable bug. Why is it?
Instance = new Singleton () is not a complete step, he is divided into these steps:
?? 1. Allocating object space;
?? 2. Initialize the object;
?? 3. Set instance to point to the address you just assigned.
?? According to these three steps, it is not a problem to feel the singleton mode, if you think so, you already understand 80%. The remaining 20% let's return to the JVM instruction rearrangement, based on JVM optimizations, these three instructions may become:
?? 1. Allocating Object space
?? 3. Set instance point to the address you just assigned
?? 2. Initializing objects
?? If you follow this order, can you imagine what the consequences would be? (Red first yellow after)
?? It is possible to return an empty object with an address, causing the exception of the program, so what can prevent reordering? Must be today's protagonist--volatile, the solution is to use volatile instance variable modification. So how does volatile prevent reordering? Let's take a look at this piece of code again
mov 0xc(%r10),%r8d ; Loadinc %r8d ; Incrementmov %r8d,0xc(%r10) ; Storelock addl $0x0,(%rsp) ; StoreLoad Barrier
?? The above Storeload barrier is what is called the memory barrier, in short, prevents the CPU disorderly access that prevents the reordering, guarantees the sequence execution of the program. Reordering is an aspect of the power of visibility in the double detection lock singleton mode? I think the answer is already in your heart. The program runs stably and robustly in the case of preventing reordering and ensuring visibility.
?? All right, the breakdown of the volatile keyword is here.
Java concurrency Programming-deep understanding of the volatile keyword