Java指令重排与多线程安全

本文探讨了Java中volatile关键字如何确保单线程内部和多线程间指令重排序的线程安全,以及锁和final字段如何提供不同级别的线程同步。通过实例和原理分析,揭示了这些概念在维护数据一致性的重要性。
摘要由CSDN通过智能技术生成

单线程内以及多线程之间的happens-before原则保证线程安全;

一、volatile保证线程安全(非原子性)

有序性(单线程内有序 / 多线程之间有序) + 可见性(一致性)
请添加图片描述

  • 单线程内:单线程中指令重排不会影响线程处理结果,但是可以使用volatile关键字禁止指令重排(volatile的单线程有序性性质,因为会在方法执行过程中相应程序行处加入内存屏障,会实时刷新缓存空间,即时编译器在寄存器中会按照顺序执行缓存行程序);

  • 多线程之间:多线程中指令重排会导致最终结果不一致,可以使用volatile关键字(用到的是volatile的一致性性质以及多线程间有序性性质) + 总线嗅探机制避免;

例子:a=b+c

首先总线嗅探保证同一时刻只有一个线程对一个变量a进行修改(其他线程中包含该变量的缓存行会失效);

其次,使用volatile关键字修饰后,各线程必须按照规定的顺序执行对该变量相关的操作(包括对该变量依赖的变量的操作a=b+c,不能以随意顺序改变a或者bc的值)—多线程有序性

最后该变量使用了volatile关键字以后线程对其的修改必须强制实时去内存中读取,修改完后立即强制刷入内存,所以每个线程得到的都是最新的变量值a,也就是说被修饰的变量经过CPU的运算后始终不会被放入寄存器中。(类似于MySQL中的当前读,不会在旧值上修改)—可见性

二、锁保证线程安全

锁也遵循happens-before原则,在一个线程释放锁时要强制刷新一遍其更新的数据到缓存。

可重入锁:当是同一个锁持有线程再次获取锁时,不需要加锁解锁,所以重入时可以省略强制刷缓存的操作,避免消耗CPU资源

三、final字段保证线程安全

final修饰的变量保证先进行赋值,然后再将该对象放入到共享的内存空间(避免变量还没有赋值完成对象就被线程引用了)。

四、补充

  1. 多线程指令执行重排序底层实现

请添加图片描述

线程1:
a = b + c
从内存读取数据 n……再执行 n = n + 1;

线程2:
d = e + f
后续指令行……

CPU执行流程:线程1先启动,执行完第一行指令后加法模块空闲,之后线程1需要从内存中读取数据n,此时线程2已经加载完e、f数据就会抢占获取加法模块,执行第一行指令

  1. volatile:轻量级,不保证原子性,但是需要频繁刷缓存,因为无法将该变量存到寄存器(变量级别)
  2. :重量级,保证原子性,不需要频繁刷缓存(方法,程序块级别)

参考文章:

https://time.geekbang.org/column/article/13484

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值