【JAVA多线程04】JMM共享模型之内存

5.1 JAVA内存模型

JMM是Java Memory Model, 它定义了主存,工作内存抽象概念,底层对应着CPU寄存器(缓存,硬件内存,CPU指令优化等)。

JMM体现在以下几个方面

原子性:保证指令不会受到线程上下文切换的影响

可见性:保证指令不会受cpu缓存的影响

有序性:保证指令不会受cpu指令并行优化的影响

5.2 可见性

循环问题

static boolean run = true;
public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(()->{
       while(run){
          // ....
       } 
    });
    t.start();
    sleep(1);
    run = false; // 线程t不会如预想的停下来
}

当线程循环获取run值到一定次数 的时候,会优化,不再从主内存中获取数据,而是会开辟工作内存存储run,提高工作效率。

解决方法

变量前面加上关键字volatile

它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作 volatile 变量都是直接操作主存

代码块加synchronized也是可以解决可见性问题但是比较重量级

可见性 vs 原子性

前面例子体现的实际就是可见性,它保证的是在多个线程之间,一个线程对 volatile 变量的修改对另一个线程可见, 不能保证原子性,仅用在一个写线程,多个读线程的情况

终止模式之两阶段终止模式 

5.3 有序性

JVM会在不影响正确性的前提下,可以调整语句的执行顺序

每个指令都可以分为:取指令,指令译码,执行指令,内存访问,数据写回这5个阶段。现代cpu支持多级指令流水线,可以同时执行上述的五个阶段,这时cpu可以同时运行五条指令的不同阶段,相当于一条执行时间最长的复杂指令,本质上不能缩短单条指令的执行时间,但是它变相提高了指令的吞吐量。

指令的重排序的前提是不能影响结果

解决方法

在变量前加上关键字volatile

volatile的底层实现原理是内存屏障,Memory Barrier

  1. 对volatile变量的写指令后会加入写屏障
  2. 对volatile变量的读指令前会加入读屏障

如何保证可见性

写屏障就是写入该变量,就把他这一行包括前面所有变量都同步到主存中

读屏障就是保证从该变量读取代码之后是加载主存的信息

 

 如何保证有序性

 double-checked locking

 但是在多线程的环境下,以上代码是不正确的,可能发生指令重排。

首先在字节码上分析,一个对象可以不用调用构造方法就赋值到变量,在上图代码中,如果一个线程只执行了赋值操作,但是对象没有调用构造方法,第二个线程突然判断是否为空,此时是不为空的,就返回一个兑现,这个时候这个对象就是没有完成初始化的对象

那怎么解决?加volatile关键字,synchronized不能防止重排序(只能防止代码逻辑没错)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值