并发中关键字的语义

it2022-05-09  28

一、volatile的内存语义

1. 简单的举例

可以把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步;但是复合操作是不生效的;

package com.youyou.ch1.demo; public class Vola { volatile int a = 1; //使用volatile 声明int类型的变量 public int getA() { return a; //对单个volatile 变量进行读 } public void setA(int a) { this.a = a; //对单个volatile 变量进行写 } public void inc() { a++; //对复合(多个)volatile 变量进行 读/写 } } View Code

这样的代用 volatile 关键字的代码和下面的是一样的;

package com.youyou.ch1.demo; public class VolaLikeSyn { int a = 0; //普通的变量 public synchronized int getA() { return a; //对单个普通 变量进行读 } public synchronized void setA(int a) { this.a = a; //对单个普通 变量进行写 } public void inc() //普通方法的调用 { int temp = getA(); //调用同步方法 temp = temp + 1; //普通的写操作 setA(temp); //调用同步的方法 } } View Code

2. volatile变量自身具有下列特性:

可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

2.1 volatile写的内存语义如下:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。

2.2 volatile读的内存语义如下:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

3. JMM对volatile的内存屏障插入策略

在每个volatile写操作的前面插入一个StoreStore屏障。在每个volatile写操作的后面插入一个StoreLoad屏障。在每个volatile读操作的后面插入一个LoadLoad屏障。在每个volatile读操作的后面插入一个LoadStore屏障。

 4. volatile的实现原理

有volatile变量修饰的共享变量进行写操作的时候会使用CPU提供的Lock前缀指令。(这个Lock指令,使我们在反编译的时候也看不见是,是汇编层面的关键字)

将当前处理器缓存行的数据写回到系统内存 ,这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

二、锁的内存语义

当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。

synchronized的实现原理:

1. 使用monitorenter和monitorexit指令实现的

monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处每个monitorenter必须有对应的monitorexit与之配对任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态sychronized“方法”通常不是用monitorenter和monitorexit指令实现的。往往是由“方法调用指令”检查常数池里的ACC_SYCHRONIZED标志

2. 了解各种锁

锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态

2.1 偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。无竞争时不需要进行CAS操作来加锁和解锁。

2.2 轻量级锁

无竞争时通过CAS操作来加锁和解锁。

2.3 重量级锁

三、final的内存语义

1. 编译器和处理器要遵守两个重排序规则。

在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

 2. final域为引用类型

增加了如下规则:在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。

3. final语义在处理器中的实现

会要求编译器在final域的写之后,构造函数return之前插入一个StoreStore障屏。读final域的重排序规则要求编译器在读final域的操作前面插入一个LoadLoad屏障

转载于:https://www.cnblogs.com/lys-lyy/p/11152452.html

相关资源:数据结构—成绩单生成器

最新回复(0)