jvm内存模型与原子性,可见性,有序性

2018-03-17  本文已影响0人  陈阳001

一.内存模型:

线程独享的工作内存和主存的关系,如下图:


image.png
  1. 当数据从主内存复制到工作存储时,必须出现两个动作:第一,由主内存执行的读(read)操作;第二,由工作内存执行的相应的load操作;
  2. 当数据从工作内存拷贝到主内存时,也出现两个操作:第一个,由工作内存执行的存储(store)操作;第二,由主内存执行的相应的写(write)操作。
  3. 每一个操作都是原子的,即执行期间不会被中断,即read不会中断,但是read和load直接会有中断。
  4. 对于普通变量,一个线程中更新的值,不能马上反应在其他变量中。如果需要在其他线程中立即可见,需要使用 volatile 关键字。
image.png

jmm控制共享变量和共享变量副本直接的拷贝,基于cpu优化的原因,会有一定程度的延迟。

二.原子性:

原子性是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
i++是原子操作吗?
不是。i++由三个原子操作组成:

  1. 线程私有内存从主存把i的值拷贝下来。
  2. 执行i++操作。
  3. 把i的值赋给主存。

三.可见性

可见性是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。
– 编译器优化
– 硬件优化(如写吸收,批操作)


image.png

这幅图展示了发生可见性问题的一种可能。如果在CPU1和CPU2

volatile:
volatile修饰的变量,在主内存和线程私有内存直接拷贝不会有延迟。

public class VolatileStopThread extends  Thread {
    private volatile boolean stop=false;
    public void stepMe(){
        stop=true;
    }

    public void run(){
        int i=0;
        while (!stop){
            i++;
        }
        System.out.println("Stop thread");
    }

    public static void main(String[] args) throws InterruptedException{
        VolatileStopThread t=new VolatileStopThread();
        t.start();//子线程
        Thread.sleep(1000);
        t.stepMe();//主线程
        Thread.sleep(1000);
    }
}

volatile 不能代替锁

四.有序性:

在并发时,程序的执行可能会出现乱序。
1.在本线程内,操作都是有序的。(不会破坏语义,所以看似有序)
2.在线程外观察,操作都是无序的。(造成原因:1.指令重排。2.主内存同步延迟--可见性)

class OrderExample {
int a = 0;
boolean flag = false;

public void writer() {
  a = 1;                   
  flag = true;           
}

public void reader() {
  if (flag) {                
      int i =  a +1;      
      ……
  }
}
}

线程A首先执行writer()方法
线程B线程接着执行reader()方法
线程B在int i=a+1 是不一定能看到a已经被赋值为1
因为在writer中,两句话顺序可能打乱:
线程A
flag=true
a=1

线程B
flag=true;在a = 1;之前执行。(此时a=0) 就是说flag=true;和a = 1;这两行很难预料谁先执行。

如何保证有序呢:加synchronized同步,同步后,即使做了writer重排,因为互斥的缘故,reader 线程看writer线程也是顺序执行的。

class OrderExample {
int a = 0;
boolean flag = false;

public synchronized void writer() {
    a = 1;                   
    flag = true;           
}

public synchronized void reader() {
    if (flag) {                
        int i =  a +1;      
        ……
    }
}
}

指令重排的基本原则:

上一篇下一篇

猜你喜欢

热点阅读