Summary:In this article, we will combine the case program to illustrate the Happens-Before principle in the Java memory model.
This article is shared from Huawei Cloud Community “[High Concurrency]Understand the Happens-Before Principle in Seconds”, author: Binghe.
Before formally introducing the Happens-Before principle, let’s look at a piece of code.
【Example 1】
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
//x的值是多少呢?
}
}
}
The above example is from:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong
Here, assuming that thread A executes the writer() method, v=true will be written into the memory according to volatile; thread B executes the reader() method, according to volatile, thread B will read the variable v from memory, if thread B reads The variable v is true, so what is the value of the variable x at this time? ?
The intuition of this sample program is that the value of x is 42. In fact, the value of x depends on the version of JDK. If the version of JDK used is lower than 1.5, the value of x may be 42 or 0. If you use JDK 1.5 and above, the value of x is 42.
Seeing this, someone will ask a question? Why is this? In fact, the answer is that the Happens-Before principle was introduced in the Java memory model in JDK1.5.
Next, we will combine the case program to illustrate the Happens-Before principle in the Java memory model.
[Principle 1]Rules of procedural order
In a thread, according to the order of the code, the previous operation Happens-Before any subsequent operation.
For example, the program x=42 in[Example 1]will be executed before v=true. This rule is more in line with single-threaded thinking: in the same thread, the modification of a variable by the program in the front must be visible to subsequent operations.
[Principle 2]volatile variable rules
A write operation to a volatile variable happens-before subsequent read operations on this variable.
That is to say, a write operation using a volatile variable occurs first before a read operation on the variable occurs later. This needs to be understood by everyone.
[Principle 3]Transmission rules
If A Happens-Before B, and B Happens-Before C, then A Happens-Before C.
Let’s look at the[Example 1]program in combination with[Principle 1][Principle 2]and[Principle 3]. At this point, we can draw the following conclusions:
(1) x = 42 Happens-Before write variable v = true, which conforms to[principle 1]program sequence rule.
(2) Write variable v = true Happens-Before read variable v = true, in line with[Principle 2]volatile variable rules.
According to[Principle 3]transmission rules, we can draw a conclusion: x = 42 Happens-Before read variable v=true.
That is to say, if thread B reads v=true, then the x = 42 set by thread A is visible to thread B. In other words, thread B at this time can access x=42.
In fact, the Java 1.5 version of the java.util.concurrent tool relies on volatile semantics to achieve visibility.
[Principle 4]Locking Rules
The unlocking operation of a lock happens-before the subsequent locking operation of the lock.
For example, the following code will automatically lock before entering the synchronized code block, and will automatically release the lock after the code block is executed.
【Example 2】
public class Test{
private int x = 0;
public void initX{
synchronized(this){ //自动加锁
if(this.x < 10){
this.x = 10;
}
} //自动释放锁
}
}
We can understand this program in this way: Suppose the value of variable x is 10, thread A changes the value of variable x to 10 after executing the synchronized code block, and releases the synchronized lock. When thread B enters the synchronized code block, it can obtain the write operation of thread A to variable x, that is, the value of variable x accessed by thread B is 10.
[Principle 5]Thread startup rules
If thread A calls the start() method of thread B to start thread B, then start() operates Happens-Before any operation in thread B.
We can also understand the thread startup rules in this way: after thread A starts thread B, thread B can see the operation of thread A before thread B is started.
Let’s look at the code below.
【Example 3】
//在线程A中初始化线程B
Thread threadB = new Thread(()->{
//此处的变量x的值是多少呢?答案是100
});
//线程A在启动线程B之前将共享变量x的值修改为100
x = 100;
//启动线程B
threadB.start();
The above code is a code fragment executed in thread A. According to[Principle 5]thread startup rules, after thread A starts thread B, thread B can see the operation of thread A before thread B is started, and access in thread B to the x variable with a value of 100.
[Principle 6]Thread Termination Rules
Thread A waits for thread B to complete (implemented by calling thread B’s join() method in thread A), and when thread B completes (thread A calls thread B’s join() method to return), thread A can access thread B’s pair of Operations on shared variables.
For example, the following operations are performed in thread A.
【Example 4】
Thread threadB = new Thread(()-{
//在线程B中,将共享变量x的值修改为100
x = 100;
});
//在线程A中启动线程B
threadB.start();
//在线程A中等待线程B执行完成
threadB.join();
//此处访问共享变量x的值为100
[Principle 7]Thread Interruption Rules
The call to the thread interrupt() method happens-before the code of the interrupted thread detects the occurrence of an interrupt event.
For example, the program code below. Before thread A interrupts thread B, modify the value of the shared variable x to 100, then when thread B detects an interrupt event, the value of the accessed variable x is 100.
【Example 5】
//在线程A中将x变量的值初始化为0
private int x = 0;
public void execute(){
//在线程A中初始化线程B
Thread threadB = new Thread(()->{
//线程B检测自己是否被中断
if (Thread.currentThread().isInterrupted()){
//如果线程B被中断,则此时X的值为100
System.out.println(x);
}
});
//在线程A中启动线程B
threadB.start();
//在线程A中将共享变量X的值修改为100
x = 100;
//在线程A中中断线程B
threadB.interrupt();
}
[Principle 8]The principle of object termination
An object’s initialization completes Happens-Before the start of its finalize() method.
For example, the program code below.
【Example 6】
public class TestThread {
public TestThread(){
System.out.println("构造方法");
}
@Override
protected void finalize() throws Throwable {
System.out.println("对象销毁");
}
public static void main(String[] args){
new TestThread();
System.gc();
}
}
The running results are shown below.
构造方法
对象销毁
Click to follow and learn about Huawei Cloud’s fresh technologies for the first time~
#Interpretation #HappensBefore #Principles #Java #Memory #Model