并发编程可见性六原则:Happens-Before 规则

并发编程中,多线程共享变量之间的可见性,可以总结为六项 Happens-Before 规则(可见性规则)。

Happens-Before 规则:前面一个操作的结果对后续操作是可见的。
六项规则:
1. 顺序性规则
前面的操作 Happens-Before 于后续的任意操作,也就是前面做的操作,后面是可见的。
2. volatile 变量规则
对一个 volatile 修饰的变量的修改操作, Happens-Before 于后续对这个 volatile 变量的读操作。
例如A线程对volatile x的值修改为2,B线程get x,能够感知到x的值变为了2。
3. 传递性
如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。
4. 同步锁的规则
对一个锁的解锁 Happens-Before 于后续对这个锁的加锁。
即相同的锁,相同的代码块,前面加锁做的操作,对后加锁都是可见的。
5. 线程 start() 规则
线程A中调用了线程B的start()方法,那么线程A调用线程B的start()方法之前的操作,对线程B都是可见的。
也就是该 start() 操作 Happens-Before 于线程 B 中的任意操作。
6. 线程 join() 规则
线程 A 调用子线程 B 的 join() 方法,当线程 B 完成后返回,线程A能够看到线程B中的操作。当然所谓的“看到”,指的是对共享变量的操作。

我们来看几个原则的示例。

规则2:

public class VolatileTest {
    int x = 0;
    volatile boolean status = false;

    public void write(String threadName) {
        System.out.println("线程进入write方法:"+threadName);
        x = 10;
        status = true;
    }

    public void read(String threadName) {
        System.out.println("线程进入read方法:"+threadName);
        if(status) {
            System.out.println(threadName +":"+ x);
        }
    }

    public static void main(String[] args) {
        VolatileTest vt = new VolatileTest();
        new Thread(()->vt.write(Thread.currentThread().getName())).start();
        new Thread(()->vt.read(Thread.currentThread().getName())).start();
    }
}

输出:

线程进入write方法:Thread-0
线程进入read方法:Thread-1
Thread-1:10

规则5:

public class ThreadStartTest {

    private int x = 0;

    public static void main(String[] args) throws InterruptedException {
        ThreadStartTest tst = new ThreadStartTest();
        Thread thread = new Thread(() -> {System.out.println(tst.getX());tst.setX(10);});
        thread.start();
        tst.setX(5);
        Thread.sleep(1000);
        System.out.println(tst.getX());
    }

    public void setX(int value) {
        x = value;
    }

    public int getX() {
        return x;
    }

}

输出:

5
10

规则6:

public class ThreadJoinTest {

    private static int x = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> x = 5);
        thread.start();
        thread.join();
        System.out.println(x);
    }
}

输出:

5