程序员一些收藏多线程java

线程安全底层原理解析

2019-09-26  本文已影响0人  降龙_伏虎

1 什么是可见性?

2 可见性原理

3 硬件层面

3.1 最大化利用CPU方法

image.png image.png

3.2 缓存(数据)一致性解决方案:

image.png image.png
image.png

M>Modified 修改状态
E>Exclusive 独享状态
S>Shared 共享状态:表示数据可被多个缓存对象进行缓存,且数据值与主内存一致
I>Invlid 失效状态
失效状态缓存不可被使用,将从主内存中进行读取

3.3 MESI的局限性

3.4 EMSI 改进

value =3;
void cup0{
  value = 10;// 通过storebuffer异步通知其他cpu缓存,将缓存value变为I:失效状态
  isFinish = true; //E 独占状态
}

void cup1{
    //由于cup0中storebuffer是异步操作
    //所以理论上村 isFinish=true 而 value=3 这种情况
    if(isFinish){//true 
        assert value == 10;//false
    }
}

storebuffer可能会导致cup的乱序执行既"指令重排序",重排序将带来可见性问题

3.5 内存屏障

value =3;
void cup0{
  value = 10;// 通过storebuffer异步通知其他cpu缓存,将缓存value变为I:失效状态
  加入内存屏障
  isFinish = true; //E 独占状态
}

void cup1{
    //由于cup0中storebuffer是异步操作
    //所以理论上村 isFinish=true 而 value=3 这种情况
    if(isFinish){//true 
        读取内存屏障//由于cup0在  'sFinish = true; //E 独占状态' 前加入内存屏障
                              //所以下面代码中value值将,直接从主内存中进行获取  
        assert value == 10;//false
    }
}

4 JMM虚拟内存模型

image.png

4.1 重排序

/**
* 无数据依赖
* 1&2行代码间无相互依赖
* 可进行从排序
*/
int a = 1;
int b = 2;


/**
* 部分数据依赖
*  1&2 行代码间无数据依赖
*  1&3 行代码间存在数据依赖
*  2&3 行代码间存在数据依赖
* 1&2行可进行重排序 1&3 2&3 行不可重排序
*/
int a=1;
int b = 2;
int c = a+b;
/**
* 单线程调用该方法时,A Happens-Befor B
**/
function X(){
 a =1;// A
 b =2;// A
}

② 【volatile规则】
volatile修饰的变量写操作一定对读操作可见,即 "写" Happens-Befor "读"
③【传递性规则】
如果 :A Happens-Befor B & B Happens-Befor C
那么: A Happens-Befor C
④ 【start规则】
主线程里的start()方法 Happens-Befo 该线程run方法内任意代码

/**
*  B Happens-Befor C (start规则)
*  A Happens-Befor B (顺序规则)
*  A  Happens-Befor C  (传递性规则)
**/
public class A{
  static x=0;
  public static  void main(String []args){
    Thread t1=new Thread(()->{
      //C .....
    });
    x=10;//A
    t1.start();//B
  }
}

⑤【Join规则】
线程run方法内代码 Happens-Befor join()后的代码

public class Demo {
    
    static  int a = 0;
    /**
     * A Happens-Befor B
    **/
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(()->{
            a = 99;//A
        });
        t1.start();
        t1.join();
        System.out.println(a);//B
    }
}

⑥ 【synchronized监视器锁规则】
synchronized 的占用顺序决定线程代码顺序

public class Demo {
    public  void  xx(){
        synchronized (this){
            //A...
        }
    }

    public static void main(String[] args) {
        
        /**
         *t1 线程t1 代码A Happens-Befor 线程t2 代码A
         *
         **/
        Demo demo = new Demo();
        Thread t1 = new Thread(()-> demo.xx());
        Thread t2 = new Thread(()-> demo.xx());
        t1.start();
        t1.join();//保证t1先于t2
        t2.start();
    }
}
value =3;
void cup0{
  value = 10;// 通过storebuffer异步通知其他cpu缓存,将缓存value变为I:失效状态
  isFinish = true; //E 独占状态
}

void cup1{
    if(isFinish){//true 
        assert value == 10;//false
    }
}

4.2 JMM解决有序性、可见性方案

5 线程的顺序执行

/**
* 只有添加join后线程才会123依次执行
**/
Thread t1 = new Thread(()->{
    //doSomething1
});
Thread t2 = new Thread(()->{
    //doSomething2
});
Thread t3 = new Thread(()->{
    //doSomething3
});
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
上一篇 下一篇

猜你喜欢

热点阅读