Java并发编程之并发关键字
volatile
保证可见性
- 一个线程修改volatile变量的值时,该变量的新值会立即刷新到主内存中,这个新值对其他线程来说是立即可见的
- 一个线程读取volatile变量的值时,该变量在本地内存中缓存无效,需要到主内存中读取
boolean stop = false;// 是否中断线程1标志
//Tread1
new Thread() {
public void run() {
while(!stop) {
doSomething();
}
};
}.start();
//Tread2
new Thread() {
public void run() {
stop = true;
};
}.start();
保证有序性
任意操作-->|不能重排序|volatile写
volatile读-->|不能重排序|任意操作
volatile写-->|不能重排序|a[volatile读]
volatile写实际上是在前面加了一个StoreStore屏障,要求不能和前面的写操作进行重排序。读操作没有要求,只是因为volatile读后面加了LoadStore屏障,因此也不能和前面的volatile读重排序。至于普通读,应该是可以进行重排的,猜测是由于读取对数据本身不产生影响,多线程情况下不存在安全问题(很多资料中都写的是前面任意操作都不能进行重排序)
boolean inited = false;// 初始化完成标志
//线程1:初始化完成,设置inited=true
new Thread() {
public void run() {
context = loadContext(); // 语句1
inited = true; // 语句2
};
}.start();
//线程2:每隔1s检查是否完成初始化,初始化完成之后执行doSomething方法
new Thread() {
public void run() {
while(!inited){ // 语句3
Thread.sleep(1000);
}
doSomething(context);
};
}.start();
线程1中,语句1和语句2之间不存在数据依赖关系,JMM允许这种重排序。如果在程序执行过程中发生重排序,先执行语句2后执行语句1,会发生什么情况?
当线程1先执行语句2时,配置并未加载,而inited=true设置初始化完成了。线程2执行时,读取到inited=true,直接执行doSomething方法,而此时配置未加载,程序执行就会有问题
不保证原子性
volatile是不能保证原子性的,可使用原子类或者加锁
volatile实现原理
- 有序性原理
StoreStore屏障-->|前屏障|volatile写
volatile写-->|后屏障|StoreLoad屏障
volatile读-->|后屏障1|LoadLoad屏障
LoadLoad屏障-->|后屏障2|LoadStore屏障
- 可见性原理
volatile写-->StoreLoad屏障
StoreLoad屏障-->|Lock前缀指令|volatile读
Lock前缀的指令将该变量所在缓存行的数据写回到主内存中,并使其他处理器中缓存了该变量内存地址的数据失效
当其他线程读取volatile修饰的变量时,本地内存中的缓存失效,就会到到主内存中读取最新的数据
- 总线风暴
基于 CPU 缓存一致性协议,JVM 实现了 volatile 的可见性。但由于总线嗅探机制,会不断的监听总线。如果大量使用 volatile,cas不断循环无效交互会导致总线带宽达到峰值,引起总线风暴。
伪共享问题
public class Share {
volatile int value;
}
volatile修饰解决了value内存可见性问题,但由于线程本地缓存是以缓存行为单位,可能会存储其他变量。因此,volatile带来的缓存失效会使同一缓存行上的其他变量也失效,访问时也需要从主从中再次获取,带来性能问题,此类问题称之为伪共享。
如何解决呢?
保证一个缓存行上只有一个share变量即可。早期版本JDK有使用无用变量作为填充物解决的,但是存在不同机器缓存行大小不一致、无用填充物被JVM优化掉等问题。基于此,在Java 8官方提供了Contended 注解,如下:
public class Share {
@Contended
volatile int value;
}
使用如上注解,需要在 JVM 启动参数中加入 -XX:-RestrictContended,这样 JVM 在运行时就会自动的为我们的 Share 类添加合适大小的填充物(padding)来解决伪共享问题。
final
基本特性
- final变量只能被赋值一次,赋值后值不再改变(引用对象地址值不能改变,但内容可以变化)
- final修饰的方法在编译阶段被静态绑定(static binding),不能被重写
- final修饰的类不能被继承,所有成员方法都会被隐式地指定为final方法
并发final
final写-->StoreStore屏障
StoreStore屏障-->B[Store 对象引用]
final变量赋值必须在所属对象引用获取前完成,通过在final写后面插入StoreStore屏障,禁止处理器把final域的写重排序到构造函数之外
A[Load 对象引用]-->LoadLoad屏障
LoadLoad屏障-->final读
相反,final变量读取必须在所属对象引用获取后完成,通过在final读前面插入LoadLoad屏障,禁止读对象引用和读该对象final域重排序
public class FinalDemo {
private int a; // 普通域
private final int b; // final域
private static FinalDemo finalDemo;
public FinalDemo() {
a = 1; // ①写普通域
b = 2; // ②写final域
}
// 线程A先执行writer()方法
public static void writer() {
// 两个操作:
// 1)构造一个FinalExample类型的对象,①写普通域a=1,②写final域b=2
// 2)③把这个对象的引用赋值给引用变量finalDemo
finalDemo = new FinalDemo();
}
// 线程B后执行reader()方法
public static void reader() {
FinalDemo demo = finalDemo; // ④读对象引用
int a = demo.a; // ⑤读普通域
int b = demo.b; // ⑥读final域
}
}
若示例中final int a变为引用类型final int[] arrays,在构造函数中初始化数组并赋值,赋值语句同样不会重排序到构造函数以外
Java并发编程之并发关键字的更多相关文章
- Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- (转)Java并发编程:volatile关键字解析
转:http://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或 ...
- Java并发编程:volatile关键字解析(转载)
转自https://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 Java并发编程:volatile关键字解析 ...
- Java并发编程:volatile关键字解析-转
Java并发编程:volatile关键字解析 转自海子:https://www.cnblogs.com/dayanjing/p/9954562.html volatile这个关键字可能很多朋友都听说过 ...
- Java并发编程:volatile关键字解析(学习总结-海子)
博文地址:Java并发编程:volatile关键字解析
- 6、Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- 转:Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字, ...
- [转载]Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- Java并发编程:并发容器之CopyOnWriteArrayList(转载)
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- Java并发编程:并发容器之CopyOnWriteArrayList
转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...
随机推荐
- springboot(六)Email demo
项目中经常使用邮件发送提醒功能,比如说更新安全机制,发送邮件通知用户等 一.简单邮件发送 导入依赖: <dependency> <groupId>org.springframe ...
- HLOD System
1.1 HLOD System简介 首先,HLOD System主要的目标是为了减少Draw Call.然后,进行更多的Batch批处理,从而大大提高渲染性能,减少面数和纹理,这样我们相应地节省了内存 ...
- PPT online viewer
PPT online viewer PPT 在线查看器 SpeakerDeck https://speakerdeck.com/xgqfrms/python?slide=3 SlideShare ht ...
- 1GB === 1000MB & 1GB === 1024MB
1GB === 1000MB & 1GB === 1024MB 字节单位换算 1 Gigabyte = 1000 Megabytes 1 Gibibyte = 1024 Mebibytes 十 ...
- iPadOS 14 memoji 无法使用 bug
iPadOS 14 memoji 无法使用 bug iPadOS 14 bug refs 如何在 iPhone 和 iPad Pro 上使用动话表情 https://support.apple.com ...
- Github & DMCA Takedown & git remove history
Github & DMCA Takedown & git remove history Github & DMCA Takedown Policy Removing files ...
- js 创建简单的表单同步验证器
SyncValidate declare const uni: any; export interface SyncValidateOpt { [key: string]: SyncValidateF ...
- Masterboxan INC金融:在区块链技术基础上推动业务模式的变革创新
10月初,2020年国际区块链技术与应用大会在硅谷开幕,全球内外区块链技术项目团队.行业领导.专家等共聚一堂,围绕区块链技术与应用展开讨论交流.美国Masterboxan INC万事达资产管理有限公司 ...
- Egg.js 是什么?
Egg.js 是什么? 阿里巴巴出 Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本. 注:Egg.js 缩写为 Egg 设 ...
- 【python接口自动化】- 正则用例参数化
我们在做接口自动化的时候,处理接口依赖的相关数据时,通常会使用正则表达式来进行提取相关的数据. 正则表达式,又称正规表示式.正规表示法.正规表达式.规则表达式.常规表示法(Regular Ex ...