并发编程-06线程安全性之可见性 (synchronized + volatile)

网友投稿 568 2022-05-29

文章目录

线程安全性文章索引

脑图

可见性定义

导致不可见的原因

可见性 -synchronized (既保证原子性又保证可见性)

可见性 - volatile(但不保证操作的原子性)

volatile变量 写操作

volatile变量 读操作

使用volatile尝试解决计数并发错误的问题 【volatile无法解决该问题】

volatile使用场景

synchronized和volatile的比较

代码

线程安全性文章索引

并发编程-03线程安全性之原子性(Atomic包)及原理分析

并发编程-04线程安全性之原子性Atomic包的4种类型详解

并发编程-05线程安全性之原子性【锁之synchronized】

并发编程-06线程安全性之可见性 (synchronized + volatile)

并发编程-07线程安全性之有序性

脑图

可见性定义

一个线程对共享变量值的修改,能够及时的被其他线程看到。

导致不可见的原因

线程交叉执行

重排序结合线程交叉执行

共享变量更新后的值没有在工作内存与主内存之间及时更新

结合我们前面说过的Java内存模型,上述三个原因我们就很容易理解了。 不清楚的童鞋可以再回顾下 并发编程-02并发基础CPU多级缓存和Java内存模型JMM

可见性 -synchronized (既保证原子性又保证可见性)

synchronized能够实现原子性和可见性 。

JMM中关于Synchronized的规定

线程解锁前,必须把共享变量的最新值刷新到主内存

线程加锁时,必须将工作内存中的共享变量的值清空,从而使用共享变量时需要从主内存中重新读取最新的值。

【注意,加锁和解锁必须是同一把锁】

可见性 - volatile(但不保证操作的原子性)

volatile可以保证 可见性和 有序性

通过加入内存屏障和禁止重排序优化来实现。

对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存

对volatile变量读操作,会在读操作前加入一条load指令屏障,从主内存中读取共享变量

volatile本质是在告诉JVM当前变量在寄存器中的值是不确定的,使用前,需要先从主存中读取,因此可以实现可见性。而对n=n+1,n++等操作时,volatile关键字将失效,不能起到像synchronized一样的线程同步(原子性)的效果。

volatile变量 写操作

并发编程-06线程安全性之可见性 (synchronized + volatile)

volatile变量 读操作

使用volatile尝试解决计数并发错误的问题 【volatile无法解决该问题】

即使将count用volatile修饰,每次从主存中取到的都是最新的值,可是当多个线程同时取到最新的值,执行+1操作,当刷新到主存中的时候会覆盖结果,从而丢失一些+1操作

volatile使用场景

多线程中使用volatile变量,对变量的写入操作不能依赖当前变量的值:如count++ .【 解释下: count++不是原子操作,因为其可以分为:从主内存中读取count的值,在自己线程的工作内存中将count的值+1,写入最新的count的值到主内存。 对于count++,线程A和线程B都执行一次,最后输出的count的值可能是1也可能是2】 。 比较适合 状态标记 场景

状态标记伪代码

volatile boolean inited = false; // 线程A context = loadContext(); inited = true; // 线程B while(!inited ){ sleep(); } doSomethingWithConfig(context);

1

2

3

4

5

6

7

8

9

10

11

将inited 标记为 volatile , 当线程A更新inited后,因为是volatile,所以线程B可以及时从主内存中感知到inited的改变。 这样就确保了线程B中使用的contex是初始化过的。

double check (比较常见的比如单例模式中的double check)

synchronized和volatile的比较

synchronized保证内存可见性和操作的原子性

volatile只能保证内存可见性

volatile不需要加锁,比synchronized更轻量级,并不会阻塞线程

volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化(如编译器重排序的优化).

volatile是变量修饰符,仅能用于变量,而synchronized是一个方法或块的修饰符。

代码

https://github.com/yangshangwei/ConcurrencyMaster

Java 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:[NAS论文][Transformer]HAT: Hardware-Aware Transformers.....
下一篇:旧貌换新颜 华为助力甲壳虫科技打造智慧环卫
相关文章