Happens-before 是啥
官网Happens-before
The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.
The synchronized and volatile constructs, as well as the Thread.start() and Thread.join() methods, can form happens-before relationships.:
Happens-before就是 保证一个线程执行的操作(写)对不同线程中的另一个操作(读)可见。
- Each action in a thread happens-before every action in that thread that comes later in the program’s order.
一个线程中,程序顺序执行每一步操作;前一步对后一步可见。 (Rule 1) - An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor. And because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor.
对一个锁的unlock操作先于后续对此锁的lock操作;前一步对后一步可见。(Rule 2) - A write to a volatile field happens-before every subsequent read of that same field. Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking.
volatile 属性写入操作先于后续对此属性的读取操作;前一步对后一步可见。(Rule 3) - A call to start on a thread happens-before any action in the started thread.
子线程启动前的任何主线程操作,都先于子线程执行;前一步对后一步可见。 (Rule 4) - All actions in a thread happen-before any other thread successfully returns from a join on that thread.
一个线程的所有操作先于对其他线程对此线程的成功返回操作(中断,等待终止);前一步对后一步可见。 (Rule 5)
例子 适配 Rule 1, Rule 4, Rule 5
package pr.iceworld.fernando.happens.before;
public class Rule1 {
/**
* 主线程直接调用
* 子线程直接调用
* 不管谁调用,都是按顺序执行
* @param a
* @param b
* @return
*/
public int calculatePlus5(int a, int b) {
int d = 5;
int c = a + b + d;
return c;
}
public int calculatePlus5ByNewThread(int a, int b, int threadIdTag) throws InterruptedException {
MyThread thread = new MyThread(a, b, threadIdTag);
System.out.println("我是主线程操作的, 子线程" + threadIdTag + "随后处理");
thread.start();
thread.join();
System.out.println("子线程" + threadIdTag + "操作完了");
return thread.getC();
}
private class MyThread extends Thread {
private int a;
private int b;
private int c;
private int theadIdTag;
public MyThread(int a, int b, int theadIdTag) {
this.a = a;
this.b = b;
this.theadIdTag = theadIdTag;
}
public void run() {
System.out.println("我是子线程" + theadIdTag + "操作的");
c = calculatePlus5(a, b);
}
public int getC() {
return c;
}
}
}
测试用例
@Test
public void testRule1Calculate() throws InterruptedException {
Rule1 rule1 = new Rule1();
Random random = new Random();
for (int i = 0; i < 1000; i++) {
Assert.assertEquals(17, rule1.calculatePlus5(5, 7));
Thread.sleep(random.nextInt(20));
Assert.assertEquals(17, rule1.calculatePlus5ByNewThread(5, 7, i));
}
}
例子适配 Rule1, Rule 2, Rule 3, Rule 4
package pr.iceworld.fernando.happens.before;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
public class Rule2 {
int size = 20;
private Object[] lockObjects = new Object[size];
int[] is = new int[size];
private boolean[] producerStarts = new boolean[size];
private boolean[] consumerStarts = new boolean[size];
private boolean[] flags = new boolean[size];
public Rule2() {
for (int i = 0; i < size; i++) {
is[i] = i;
producerStarts[i] = true;
if (producerStarts[i]) {
consumerStarts[i] = true;
}
flags[i] = false;
lockObjects[i] = new Object();
}
}
final Queue<String> queue = new ArrayBlockingQueue<>(size * 4);
public Queue<String> getQueue() {
return queue;
}
public void doSynchronize() {
Arrays.stream(is).forEach( e -> {
Thread consumer = new Thread("consumer" + e) {
public void run() {
queue.add("consumerStarts[" + e + "] = " + consumerStarts[e]);
while (true) {
synchronized (lockObjects[e]) {
if (flags[e]) {
flags[e] = false;
queue.add("consumer " + e + " verified the flag");
}
}
}
}
};
consumerStarts[e] = true;
consumer.start();
});
Arrays.stream(is).forEach(e -> {
Thread producer = new Thread("producer" + e) {
public void run() {
queue.add("producerStarts[" + e + "] = " + producerStarts[e]);
synchronized (lockObjects[e]) {
flags[e] = true;
queue.add("producer " + e + " changed flag");
}
}
};
producerStarts[e] = true;
producer.start();
});
}
}
测试用例
@Test
public void testRule2DoSynchronize() throws InterruptedException {
Rule2 rule2 = new Rule2();
rule2.doSynchronize();
// 方便线程执行完成
Thread.sleep(2000);
rule2.getQueue().stream().forEach(System.out::println);
System.out.println("done");
System.out.println("---------------");
}