preface
There was A problem where we had A shared variable keepRunning=true, while (keepRunning) in thread A; , thread B executes keepRunning = false; Start both threads A and B in the main function, and you will find that the program will continue to run without exiting. In plain English, this is A typical visibility problem. Thread A does not know that keepRunning has been modified, so it does not read the value of the modified keepRunning variable from main memory into the thread cache.
For example,
The above problem is equivalent to the following code snippet:
1/ * *
2 * @author mars_jun
3* /
4public class NoVisibility_Demonstration extends Thread {
5 boolean keepRunning = true;
6
7 public static void main(String[] args) throws InterruptedException {
8 NoVisibility_Demonstration t = new NoVisibility_Demonstration();
9 t.start();
10 System.out.println("start: " + t.keepRunning);
11 Thread.sleep(1000);
12 t.keepRunning = false;
13 System.out.println("end: " +t.keepRunning);
14 }
15
16 public void run(a) {
17 int x = 1;
18 while (keepRunning) {
19 // system.out.println (" If you do not comment this line, the program will stop normally!" );
20 x++;
21
22 }
23 System.out.println("x:" + x);
24 }
25}
Copy the code
Run the code directly, and you will see that after printing end: false, the program does not exit normally. Instead, it keeps running in a while (keepRunning) loop. But we tried to comment system.out.println (” If you don’t comment this line, the program will stop!” ); Uncomment and run the above code again, and you will see that the program will run for some time and then exit normally. Println (system.out.println), thread T reads t.keeeprunning = false from the main thread, causing the while loop to exit. I’m going to have to look at the source code for println.
1 public void println(String x) {
2 synchronized (this) {
3 print(x);
4 newLine();
5 }
6 }
Copy the code
So what we’re going to see here is that the println method is a synchronous method. It is well known that methods or blocks of code decorated with the synchronized keyword ensure serial execution (only one thread can acquire execution permission at a time), In Doug Lea’s Concurrent Programming in Java, there is an excerpt describing the synchronized keyword:
In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields. While lock actions provide exclusion only for the operations performed within a synchronized method or block, these memory effects are defined to cover all fields used by the thread performing the action.
Essentially, releasing a lock forces all previous writes in the working memory to be flushed to main memory, while acquiring a lock forces loading of accessible values into the thread’s working memory. Although the lock operation only affects the synchronized method and synchronized code block, it affects all the fields used by the thread to perform the operation. Println (” If you don’t comment out this line, the program will stop normally! “) ); After this, thread T can read the value of the modified keepRunning. For this issue, some people say that printing is an IO operation, and the IO operation will cause a thread switch, which will invalidate the original cache of the thread and thus read the modified value. File File = new File(“G://1.txt”); This code, the program can also end normally. Of course, here you can also try to replace the printing with synchronized(NoVisibility_Demonstration. Class){} empty synchronization code block and find that the program can also end normally.
conclusion
At the very least, the thread’s cache is flushed when an IO operation or a synchronized modified method or block of code is invoked internally, and changes in shared variables are sensed. Of course, this applies only to variables that are not volatile; when declared as volatile, each use of the variable is read from main memory. (For those unfamiliar with volatile, check out my article on how volatile works and uses it.)
conclusion
Changes made by one thread to a field can be guaranteed to be visible to other threads only if:
- The writer thread releases the lock, and the reader thread subsequently acquires the same lock. All writes are forced to be flushed from the working memory used by the thread when the lock is released, and the values of the accessible fields are forced to be reloaded when the lock is acquired.
- If a field is declared volatile, the writer thread immediately synchronizes the changed value to main memory. The reading thread must reload the value of the volatile field on each access.
- The first time a thread accesses a field of an object, it sees the initial value of the field or a value written by some other thread.
- When a thread terminates, all written variables are flushed to main memory. For example, if thread A and thread B call a.jin () from thread B, then thread B is guaranteed to see the effect of thread A.