JVM-Java内存模型(JMM)

3 篇文章 0 订阅
2 篇文章 0 订阅

Java内存模型(JMM)

JMM 可以屏蔽掉各种硬件和操作系统的内存访问差异,让Java程序在各个平台下都能达到一致的内存访问效果。

1. 主内存与工作内存

JMM规定所有的变量(包括实例变量、静态字段、构成数组对象的元素)都存储在主内存中,每个线程还有自己工作内存。线程工作内存保存了被该线程使用的变量的主内存的副本拷贝(不会拷贝整个对象,可能拷贝的是对象的引用、所对象访问的字段),线程对变量的操作都是在工作内存中进行,线程之间的值传递需要通过主内存完成。

2. 内存间交互操作

工作内存和主内存间的交互操作:一个变量如何从主内存拷贝到工作内存,在如何从工作内存同步到主内存。

JMM中定义了8中原子性操作来完成。

操作含义作用区域作用
lock锁定主内存变量把一个变量标识为一条线程独占状态
unlock解锁主内存变量把一个处于锁定的变量释放出来
read读取工作内存变量把一个变量从主内存传输到线程的工作内存
load载入工作内存变量把read操作的变量值放入工作内存副本中
`use使用工作内存变量把工作内存中的一个变量的值传递给执行引擎,当JVM遇到一个需要变量的值得字节码指令时将会执行这个操作
assign赋值工作内存变量把从执行引擎接受到的值给工作内存的变量,当JVM遇到一个给变量赋值的额字节码指定时执行该操作
store存储工作内存变量把工作内存中的一个变量的值传递到主内存中
write写入主内存变量store操作传递的值放入工作内存的变量中
  • 变量复制主内存->工作内存:顺序执行readload
  • 变量同步工作内存->主内存:顺序执行store、write

上面两过程不要必须连续执行,只要顺序执行,且每个操作不得单独出现。

3. Volatile登场

  • 可见性

volatile修饰的变量,对所有的线程是可见的。即当一个线程修改了该变量,其他线程都是立即可见的。但是,并不表示volatile变量是支持线程安全的。

public static volatile  int count = 0;
    private  static final int  THREAD_COUNT = 30;

    public  static  void increase(){
        count++;
    }

    public static void main(String[] args){
        Thread[] threads = new Thread[THREAD_COUNT];

        for (int i = 0 ;i<THREAD_COUNT;i++){
            threads[i]= new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i=0;i<1000;i++){
                        increase();
                    }
                }
            });
            threads[i].start();
        }
        //等待所有线程执行完毕
        while (Thread.activeCount()>1){
            Thread.yield();
        }
        System.out.println(count);
    }

在这里定义了30个线程去累加count,每个线程加1000,理想在并发状态下得到的应该是30000,实际输出来看下

实际得到的数值会比预计的要小。

由于这里使用count++来累加改变这个volatile变量,而自增操作并不是原子性的,即使count变量是所有线程可见,但不能保证在并发执行运算的时候,非原子性操作一定是读取的和和主内存变量同步的值。

  • 禁止指令重排优化

普通变量仅仅能保证在执行该方法的过程中所有依赖赋值的地方能得到正确的结果,但不能保证程序一定按照代码的顺序执行

看个伪代码

//定义一个标识
boolean flag = false;
//业务代码A
service.changeState();
//业务代码执行完,改变flag
 flag = true;


//.....

//当flag为true时,执行另一段业务B
while(flag){
    //dosomething
}

由于系统有指令优化重排,获取flag=true可能会被提前执行,也就是在业务A没有执行情况下,就已经改变了flag。实际上我们想要的是在业务A执行完之后在区执行flag=true

//定义一个标识
volatile boolean flag = false;
//业务代码A
service.changeState();
//业务代码执行完,改变flag
 flag = true;


//.....

//当flag为true时,执行另一段业务B
while(flag){
    //dosomething
}

加了volatile就可以让代码按序执行

4. 先行发生原则(happents-before)

先行发生是JMM中定义的两项操作之间的偏序关系,如果说操作A先行于操作B发生,其实就是说在操作B发生之前,操作A产生的影响能被操作B观察到。

这里的影响包括修改了内存中共享变量的值、发送了消息、调用了方法等。

//thread A 执行
i=1;
//thread B 执行
j=i;
//thread C 执行
i=4;

假设线程A会先于B执行,那么当只执行线程A、B时,A永远先于B执行,所以j一定等于1。若在A、B之间加入线程C执行,并没有规定C会先于B发生,所以此时j的值就会不确定,线程不安全。

下面介绍下Java中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值