This is the fourth day of my participation in the August Text Challenge.More challenges in August
Code that affects memory visibility
WangScaler: A writer with heart.
Declaration: uneducated, if there is a mistake, kindly correct.
Do we frequently write system.out.println () to verify program execution during development? Remember to delete useless print statements in the formal environment. Why is that? Because system.out.println () is a synchronous operation, it affects performance.
Memory visibility
Visibility refers to whether when one thread changes the value of a shared variable, other threads are immediately aware of the change.
We know that Shared variables are stored in main memory, each thread USES the copy from main memory to their working memory, so the thread when operating this variable is memory, in their own work finished will value set to the main memory, that is the value of the other threads to refresh the main memory, and our perception of the current thread is not, We continue to manipulate the values of our own workspace, which eventually leads to shared variables in main memory not being what we expected
An example is given to illustrate the invisibility of memory.
package com.wangscaler.jmm;
import java.util.concurrent.TimeUnit;
/ * * *@author WangScaler
* @date2021/8/3 13:53 * /
public class Visibility {
boolean flag = true;
public void changeFalse(a) {
this.flag = false;
}
public static void main(String[] args) throws InterruptedException {
Visibility visibility = new Visibility();
new Thread(() -> {
System.out.println("Change flag to FLase after 3s");
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
visibility.changeFalse();
System.out.println("The ChangeFalse thread changes flag to:" + visibility.flag);
}, "ChangeFalse").start();
while (visibility.flag == true) {
}
System.out.println("The main thread gets a false flag."); }}Copy the code
I’m done printing this example
3s later, the flag is changed to Flase ChangeFalse. The flag value of the thread is changed to falseCopy the code
It goes into an infinite loop. We see that the ChangeFalse thread has changed its flag to false, but the thread has not been terminated, which confirms our statement.
- The main thread copies the value of flag from main memory into its own working memory.
-
The thread ChangeFalse is then started, and the thread copies the value of flag from main memory into its working memory.
-
Then the while loop reads the value of flag from its working memory, always true, and keeps repeating.
-
3s later, the thread ChangeFalse changed the value of flag from its working memory to false.
-
Although the value of the thread ChangeFalse working memory flag has been changed, it is uncertain when to flush to main memory
-
Even if flushed to main memory immediately, other threads are not aware of it.
-
So the flag that the while loop keeps reading from its own working memory is in an infinite loop
How do you guarantee visibility
This certainly does not meet our expectations, so how can we achieve the goal we want? Use the keyword volatile!
Change the above code to Boolean flag = true; Change to volatile Boolean flag = true; Can.
After using this keyword, when the ChangeFalse thread changes the flag, it will immediately refresh to main memory and invalidate the value of the working memory of other threads, thus pulling the new flag value from main memory. Therefore, when the ChangeFalse thread changes the flag, The main thread terminates the loop when it senses that a new value is immediately pulled from main memory.
case
The classic case is the singleton pattern, we in design pattern five —- singleton pattern has been told, here no longer repeat, interested friends can go to see.
Effect of system.out.println () on visibility
package com.wangscaler.jmm;
import java.util.concurrent.TimeUnit;
/ * * *@author WangScaler
* @date2021/8/3 13:53 * /
public class Visibility {
boolean flag = true;
public void changeFalse(a) {
this.flag = false;
}
public static void main(String[] args) throws InterruptedException {
Visibility visibility = new Visibility();
new Thread(() -> {
System.out.println("Change flag to FLase after 3s");
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
visibility.changeFalse();
System.out.println("The ChangeFalse thread changes flag to:" + visibility.flag);
}, "ChangeFalse").start();
while (visibility.flag == true) {
System.out.println("----------------- in the loop ------------------");
}
System.out.println("The main thread gets a false flag."); }}Copy the code
Visibility is achieved simply by adding a system.out.println () to the while loop, so try to remove the useless System.out.println() in production.
Why is it that our scribbled print-outs have an unpredictable effect on test results? With curiosity, I opened the source code.
public void println(String x) {
synchronized (this) { print(x); newLine(); }}Copy the code
Yes, the familiar print statement is a synchronous method. Synchronized () guarantees both visibility and atomicity, so it is self-evident that the use of print statements causes loop termination to stop. Does system.out.println () affect synchronized()? Let’s verify that.
Effects of synchronized() on visibility
package com.wangscaler.jmm;
import java.util.concurrent.TimeUnit;
/ * * *@author WangScaler
* @date2021/8/3 13:53 * /
public class Visibility {
boolean flag = true;
public void changeFalse(a) {
this.flag = false;
}
public static void main(String[] args) throws InterruptedException {
Visibility visibility = new Visibility();
new Thread(() -> {
System.out.println("Change flag to FLase after 3s");
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
visibility.changeFalse();
System.out.println("The ChangeFalse thread changes flag to:" + visibility.flag);
}, "ChangeFalse").start();
while (visibility.flag == true) {
synchronized (Visibility.class) {
}
}
System.out.println("The main thread gets a false flag."); }}Copy the code
As expected, the program terminates normally, thus confirming that system.out.println () is the source of visibility caused by synchronized.
TimeUnit.MILLISECONDS.sleep(5); Impact on visibility
package com.wangscaler.jmm;
import java.util.concurrent.TimeUnit;
/ * * *@author WangScaler
* @date2021/8/3 13:53 * /
public class Visibility {
boolean flag = true;
public void changeFalse(a) {
this.flag = false;
}
public static void main(String[] args) throws InterruptedException {
Visibility visibility = new Visibility();
new Thread(() -> {
System.out.println("Change flag to FLase after 3s");
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
visibility.changeFalse();
System.out.println("The ChangeFalse thread changes flag to:" + visibility.flag);
}, "ChangeFalse").start();
while (visibility.flag == true) {
TimeUnit.MILLISECONDS.sleep(5);
}
System.out.println("The main thread gets a false flag."); }}Copy the code
After testing is not only a synchronized method visibility, using TimeUnit. MILLISECONDS. Sleep (5); The same expectation would be met. You might say that the source code also uses synchronization, but looking through it doesn’t seem to turn up. The consensus is that threads refresh working memory values in main memory when they are idle.
While TimeUnit. MILLISECONDS. Sleep (5); Sleep (ms, ns); Thread.sleep(ms, ns); It will have the same effect.
public void sleep(long timeout) throws InterruptedException {
if (timeout > 0) {
long ms = toMillis(timeout);
intns = excessNanos(timeout, ms); Thread.sleep(ms, ns); }}Copy the code
conclusion
1. Visibility is guaranteed with synchronized().
2. The JVM synchronizes shared variables in main memory as much as possible at idle time.
Some online claims also suggest that the visibility caused by the first synchronization mechanism is actually an illusion caused by the second one, text links. We just need to know that these two cases will cause the visibility of memory, exactly what causes it, interested friends can have a deeper understanding, if you know the answer, please let me know the comment, thank you very much.