"The prime does not start again, a day difficult again morning, timely encouragement, the years do not treat people." " Tao Yuanming
We already know that synchronous code blocks and synchronous methods are guaranteed to be executed atomically, but there is another important concept of synchronization: memory visibility. In other words, not only do we want to prevent a thread from using object state while another thread is modifying the state at the same time, and you want to ensure that when a thread modifies the state of an object, other threads can see the modified state.
Visibility of
A thread changes the value of a shared variable to be seen by other threads in a timely manner. Visibility is subtle, because the things that can happen wrong are always very different from intuition. Take a look at the following example and his results:
1PublicClassnovisibility {
2PrivateStaticBoolean ready;
3PrivateStaticint number;
4PrivateStaticClassReaderthreadExtendsThread {
5PublicvoidRun() {
6while (!ready)
7 Thread.yield ();
8 System.out.println (number);
9}
10}
11 public static Span class= "Hljs-keyword" >void main (string[] args) {
12 //TODO auto-generated Method stub
13 new readerthread (). Start ();
14 number = 88;
15 ready = true;
16}
17}
The above code listing, the result of pro-test execution is 88.
However, the explanation in the book is that there may be false results. The result of the error is in the following two cases (I cannot reproduce the following result):
- The novisibility may keep looping because the read thread says that the value of the main thread to ready may never be visible to the read threads.
- Novisibility may print 0 because the main thread has already written to ready and made it visible to the read thread before the number is assigned, which is a reordering.
That is, the amiable test does not occur, but may occur. To prevent this from happening, you can only synchronize the shared variables appropriately.
Java memory model (Jmm,java)
Describes the access rules for various variables (thread-shared variables) in Java programs, as well as the underlying details of storing variables in the JVM into memory and reading out variables from memory.
Write a picture description here
All variables are stored in main memory and each thread has its own independent working memory, which holds a copy of the variable used by the thread, which is a copy of the variable in main memory.
All operations of a thread on a shared variable must be in its own working memory, and the passing of variable values between threads needs to be done through main memory.
Locking and visibility
The meaning of locking is not limited to mutex behavior, but also includes memory visibility. To ensure that all threads are able to see the latest values of shared variables, all read or write operations must be synchronized on the same lock.
Write a picture description here
When thread B executes a lock-protected block of code, you can see all the results of the operation in the same synchronized code block before thread A. This is why all threads are required to synchronize on the same lock, in order to ensure that the value that a thread writes to the variable is visible to other threads.
Non-atomic 64-bit operation
The JVM allows a 64-bit read or write operation to be decomposed into two 32-bit operations. Long and double types in Java are 64-bit, so when reading a long variable of a non-volatile type, if the variable's read and write operations are performed in a different thread, it is likely to read the high 32 bits of a value and the lower 32 bits of the other value. Therefore, it is not safe to use shared mutable long and double type variables in multi-threading unless they are declared with the keyword volatile, or protected with locks.
Volatile variable
Java provides a slightly weaker synchronization mechanism, the volatile variable, that is used to ensure that updates to variables are notified to other threads. Volatile variables have synchronized visibility, but do not have atomic properties. For the volatile variable to provide ideal thread safety, the following two conditions must be met:
- A write to a variable does not depend on its current value
- The variable is not included in the invariant with other variables
Volatile is often used as a marker for identity completion, interrupts, and status. Typically, the following code is used to check the status tag to determine whether to exit a loop.
1volatile boolean asleep;
2 while(!asleep)
3 countSomeSheep();
Of course, locks can also be used above, but it complicates the code. Volatile variables are not locked and do not cause thread blocking, which is a lightweight synchronization mechanism compared to sychronized. Although volatile can also be used to identify other types of state information, be cautious. For example, the semantics of volatile are not enough to make the self-increment operation (count++) atomized.
[Java Concurrent Programming combat] the visibility of shared objects