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);

    }

}

start结果

join结果

posted on 2019-03-31 11:14  唠叨叨  阅读(158)  评论(0编辑  收藏  举报