竹笋

首页 » 问答 » 灌水 » JUC源码原子类,CAS,Volati
TUhjnbcbe - 2022/8/28 19:09:00

volatile的原理和内存屏障参考《Java并发编程的艺术》原子类源码基于JDK8

一丶volatile与内存屏障

volatile修饰的字段,Java线程模型保证所有线程看到这个变量值是一致的。

1.volatile是如何保证可见性

volatile修饰的变量执行写操作的时候多出lock前缀指令的代码,lock前缀的指令会导致

将当前这个处理器缓存行的数据写回到系统内存

这个写回内存的操作将导致其他CPU里缓存了该地址内存的数据无效

为了提高处理速度,处理器不直接和内存通信,而是先把系统内存的数据读到内部缓存后继续操作,但是操作完不知道何时写回内存。如果对volatile修饰的变量执行写操作,将会让数据写回到系统内存,但是其他线程还是使用缓存中的旧值,还是会存在问题。所以在多处理器下为了保证每一个处理器缓存时一致的,就会实现缓存一致性协议,每个处理器通过嗅探总线上传播的数据来检查自己缓存的数据是否过期,如果发现自己缓存行中对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效,当前处理器对这个数据进行修改操作时,会重新从主内存拉取最新的数据到缓存。

2指令重排序

在程序执行时,为了提高性能,处理器和编译器通常会对指令进行重排序。

编译器优化重排序,编译器在不改变语义的情况下,重新安排语句执行顺序。

指令级别并行重排序,如果不存在数据依赖性,处理器改变语句对应机器指令的执行顺序。

内存系统重排序,由于处理器使用缓存和读/写缓冲区,这使得加载和存储的操作看起来是乱序执行

为了保证内存可见性,Java编译器在生成指令序列的适当位置会插入内存屏障来禁止处理器级别的(指令级别并行重排序,内存系统重排序)指令重排序

3JMM中内存屏障的类型

不同硬件实现内存屏障的方式不同,Java内存模型屏蔽了这种底层硬件平台的差异,由JVM来为不同的平台生成相应的机器码。

LoadBarrier:在读指令前插入读屏障,可以让高速缓存中的数据失效,重新从主内存加载数据

StoreBarrier:在写指令之后插入写屏障,能让写入缓存的最新数据写回到主内存

实际使用中,又分为以下四种:

4.volatile的内存语义

可见性:对一个volatile变量的读一定能看到(任何线程)对这个volatile变量最后的写

原子性:对任意单个volatile变量的读和写具有原子性,但是自增这种复合操作不具备原子性

5.volatile内存语义的实现

JMM为了实现volatile的内存语义限制了编译器重排序和处理器重排序

当第一个操作是普通变量都或者写且第二个操作是volatile写时,编译器不能重排序这两个操作

当第二个操作是volatile写时,无论第一个操作是什么都不可以重排序,保证了volatile写操作前的指令不会重排序到volatile写之后

当第一个操作是volatile读时,不管第二个操作是什么,都不可重排序,保证了volatile读之后的指令不会重排序到volatile读之前

当第一个操作是volatile写,第二个操作是volatile读,不能重排序

为了实现volatile的内存语义,JMM在volatile读和写的时候会插入内存屏障

volatile写的内存屏障

这里的storestore屏障可以保证前面所有普通写对所有处理器可见,实现了在volatile写之前写入缓存的最新数据写回到主内存

volatile写之后的内存屏障,避免与后续的volatile读写出现重排序,由于虚拟机无法判断volatile写之后是否需要一个storeload屏障,比如在volatile写之后立即return,为了保证volatile的内存语义,JMM十分保守的插入一个storeload屏障。

volatile读的内存屏障

这里的loadload保证了下面普通读不可以在volatile读之前,loadstore保证普通写不可在volatile之前

二丶CAS

1.什么是CAS

即比较并替换,实现并发算法时常用到的一种技术。CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe提供的CAS方法(如

1
查看完整版本: JUC源码原子类,CAS,Volati