1.内存模型概览
目标
可见性和有序性
定义多项规则对编译器和处理器进行限制
Happens-Before规则
1.规则1:程序顺序 防止编译优化(volatile)
一个线程中,按照程序顺序,前面的操作 Happens-Before后续操作
2.规则2 :volatile 变量规则 (保证可见行)
对一个 volatile 变量的写操作, Happens-Before 后续 对该变量的读操作
后续指的是时间上的后续,而不是程序顺序
3.规则3:传递性
op1<<op2,op2<<op3 =>op1<<op3
4.规则4 锁定
一个锁的解锁 Happens-Before 于后续对这个锁的加锁
5.规则5: start() 规则
线程 A 调用线程 B 的 start() 方法,那么该 start() 操作 Happens-Before 于线程 B 中的任意操作
6.规则6:join规则
线程 B 中的任意操作 Happens-Before 于主线程中B.join() 操作的返回
实现
通过内存屏障(memory barrier)禁止重排序的
对于volatile,编译器将在volatile字段的读写操作前后各插入一些内存屏障。
java demo代码可见 https://github.com/sarafina527/JavaPuzzle
2.规则1-3
package concurrency;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* volatile 保证读写线程的
*/
public class VolatileExample implements Runnable{
int x = 0;
// volatile 变量规则
volatile boolean v = false;
public void writer() {
/**
* 规则1:程序顺序 防止编译优化(volatile)
* 一个线程中,按照程序顺序,前面的操作 Happens-Before后续操作
* 比如此处同一个线程执行过程中 x的赋值永远 先于 v的赋值
* x = 42 << v=true
*/
x = 42;
System.out.println(Thread.currentThread().getName()+" wx :" + x);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
v = true;
System.out.println(Thread.currentThread().getName()+" wv :" + v);
}
public void reader() {
System.out.println(Thread.currentThread().getName()+" rv :" + v);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 规则2 :volatile 变量规则 (保证可见行)
* 对一个 volatile 变量的写操作, Happens-Before 后续 对该变量的读操作
* v=true << v==true
* 如此处 run()中先writer()后read() ,所以即使读线程,读v永远在写线程的写v后面执行
*/
if (v == true) {
/**
* 规则3 :传递性
* x=42 << v=true;v=true << v == ture => x=42 << v==true
* 所以此处的x一定时42 ,读线程对写线程x修改 可见
*/
System.out.println(Thread.currentThread().getName()+" rv :" + v);
System.out.println(Thread.currentThread().getName()+" rx :" + x);
}
}
public void run() {
writer();
reader();
}
public static void main(String[] args) {
Executor executor = Executors.newFixedThreadPool(2);
VolatileExample task = new VolatileExample();
// 分别启动读线程和写线程 程序正确性 :保证读线程取x时永远应是42,而非0
executor.execute(task.new WriteTask());
executor.execute(task.new ReadTask());
}
class WriteTask implements Runnable {
public void run() {
writer();
}
}
class ReadTask implements Runnable {
public void run() {
reader();
}
}
}
执行结果
3.规则5-6
package concurrency;
import org.junit.Test;
/**
* 1.规则5 start() 规则
* 2.规则6 join() 规则 线程等待
*/
public class ThreadExample {
private static int var;
//规则5: start() 规则
// 线程 A 调用线程 B 的 start() 方法,那么该 start() 操作 Happens-Before 于线程 B 中的任意操作
// var=77 << B.start() <<B线程中System.out.println(var); => 打印出77
@Test
public void testStart() {
Thread B = new Thread(new Runnable() {
public void run() {
// 主线程调用 B.start() 之前
// 所有对共享变量的修改,此处皆可见
System.out.println(Thread.currentThread().getName() + "read var:" + var);
}
});
// 主线程对共享变量 var 修改
var = 77;
System.out.println(Thread.currentThread().getName()+ " write var=" + var);
// 主线程启动子线程
B.start();
}
/**
* 规则6:join规则
* 线程 B 中的任意操作 Happens-Before 于主线程中B.join() 操作的返回
*/
@Test
public void testJoin() throws Exception{
Thread B = new Thread(new Runnable() {
@Override
public void run() {
// 此处对共享变量 var 修改
var = 66;
System.out.println(Thread.currentThread().getName()+ " write var=" + var);
}
});
B.start();
B.join(); //那么线程 B 中的任意操作 Happens-Before 于join() 操作的返回
// 在主线程调用 B.join() 之后皆可见
// 此例中,var==66
System.out.println(Thread.currentThread().getName()+" read var:" + var);
}
}