线程安全性三大特性

这篇文章之前发过,因为忘了标记原创,所以再发一次,想加入并发群的加我微信:guofu-angela,备注并发。

大家好,这是并发编程的第六集,前五集如下:

完整大纲如下:

线程安全性三大特性


面试官:你好,你先自我介绍一下吧。

安琪拉:面试官你好,我是草丛三婊,最强中单,火球拥有者、不焚者,安琪拉,这是我的简历,请过目。

面试官:听上一个面试官说你Java并发这块掌握的不错,我们深入的交流一下;

安琪拉:好好好,可以交流深入

面试官:并发编程三大问题的是哪三个?

安琪拉:【这题上次不是问了嘛】原子性、可见性、有序性

面试官:解释一下引发这三个问题的原因?

安琪拉:那我讲下这三个问题引发的原因,实际上并发控制都是围绕解决这三个问题展开的。

  • 在 《并发与高并发系列第三集-Java内存模型》 里面讲过缓存的应用,因为缓存(CPU缓存行、寄存器)的更新不是及时的(出于性能方面的设计),在多线程环境里面,如下图,一个线程(A线程)对主内存的修改不能及时地被其他线程看到,引发了可见性问题。

    线程安全性三大特性

  • 多线程处理会有线程切换,一个线程一组指令执行过程中发生线程切换,数据还不是预期的终态,只是中间态,这时另一个线程执行,由于一组指令的非原子性执行导致的真实执行结果和预期不符,原子讲的就是执行不可中断,要么一组操作都成功执行完成,要么都不执行。

  • 编译器为了优化会对执行指令做重排序,重排序有原则,那就是as-if-serial, as-if 英文翻译过来就是“就如同”,as-if-serial 指的是就如同单线程一样,也就是重排序执行的结果和单线程执行结果一样。但是在多线程环境,指令重排也会引发线程不安全的问题,例子我就不举了,网上一大堆。

    线程安全性三大特性
    image-20200511163223157

    下图是各种CPU 架构允许的指令重排序的情况。

    线程安全性三大特性
    image-20200511165457535

    关于store和load,不清楚的看并发系列历史文章,也许是东半球最叼的Java内存模型章节。

面试官:上次原子性已经说完了,这次说说可见性问题的现象吧,

安琪拉:可见性问题描述:多线程环境下,一个线程对某个共享变量进行更新之后,后续访问该变量的线程可能无法立刻读取到这个更新的结果,这就是线程安全问题的另外一个表现形式:可见性

面试官:那你说说synchronized、volatile等关键字怎么解决可见性问题的呢?

安琪拉

synchronized:

  1. 线程解锁前,必须把共享变量的最新值刷新到主内存。
  2. 线程加锁前,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁和解锁是同一把锁

关于工作内存,可以参考之前Java内存模型的文章,每个线程有自己私有的工作内存空间。

volatile:

二方面,内存屏障指令会禁止指令重排序,并且让缓存数据及时更新到共享主存,获取数据直接从共享主存而不是缓存获取。

实现方式:

  1. 对volatile变量进行写操作时,会在写操作后加入一条store屏障指令(store屏障指令不是store指令),将本地内存中的共享变量值刷新到主存中。
  2. 对volatile变量进行读操作时,会在读操作前加入一条load屏障指令(load屏障指令不是load指令),从主存中读取共享变量。

内存屏障指令不复杂,看下面的指令表,就是load和store的组合而成,用于让指令不要重排,并且前面的指令执行到位,再执行后面的指令。

JVM中提供了四类内存屏障指令:

线程安全性三大特性
image-20200512091721797

JSR-133 定义的相应的内存屏障,在第一步操作(列)和第二步操作(行)之间需要的内存屏障指令如下:

线程安全性三大特性
image-20200511174714486

Java  volatile 实际例子:

线程安全性三大特性
image-20200511175002261

以下是区分各个CPU体系支持的内存屏障(也叫内存栅栏),由JVM 实现平台无关(volatile所有平台表现一致)

线程安全性三大特性
image-20200511172853931

面试官:JMM(Java内存模型)怎么解决有序性问题的呢?

安琪拉:我分别从三个层面,从下往上讲

  • 编译器防重排:JMM会禁止特定类型的编译器重排序。
  • 加内存屏障指令:JMM要求Java编译器在生成指令时,在某些指令之间插入特定的内存屏障指令,用于禁止指令重排。
  • Java语言支持:Java提供了一些关键字volatile、synchronized,应用这些关键字,JMM要求插入内存屏障指令。

想加入发群的加我微信:guofu-angela,备注并发

原文始发于微信公众号(安琪拉的博客):线程安全性三大特性

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/25133.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!