p160
初探JMM内存模型
结合volatile实现线程优雅停止
有序性
volatile如何保证可见性
CAS
概念:
JMM 即 Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着 CPU 寄存器、缓存、硬件内存、CPU 指令优化等。
JMM 体现在以下几个方面:
- 原子性 - 保证指令不会受到线程上下文切换的影响 (synchronized,lock)
- 可见性 - 保证指令不会受 cpu 缓存的影响 (synchronized,lock,volatile)
- 有序性 - 保证指令不会受 cpu 指令并行优化的影响 (synchronized,lock,volatile)
注意:synchronized , lock虽不能禁止指令重排,但能保证有序性.
可见性案例:
//退不出的循环
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(run){
// ....
}
});
t.start();
sleep(1);
run = false; // 线程t不会如预想的停下来
}
正确做法:static volatile boolean run = true;
注意: synchronized 语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性。但缺点是synchronized 是属于重量级操作,性能相对更低。
如果实际中的情况是只有一个线程在写操作,多个线程读操作,适合用volatile。
结合volatile实现线程优雅停止
为什么要保证有序性:
为了提高cpu的执行效率,jvm会优化指令的执行顺序。在单线程的情况下,这样是没有问题的。但是在多线程的情况下就会出现安全性问题。
- 对volatile变量的写指令之后会加入写屏障。
当被对volatile修饰的共享变量被修改后,会立刻同步到主存。 - 对volatile变量的读指令之前会加入读屏障。
当要获取被volatile修饰的共享变量时,会直接读取主存中的数据。
CAS 参考链接
概念:CAS又叫**“比较并交换(compare and set)”,CAS是一种基于乐观锁的实现**,他的目的是通过不加锁的方式来对共享资源的保护。CAS的基本思想是,将工作内存中旧的预期值与主存中的值进行比较,如果相同,则认为主存中的值没有被修改,那么就将工作内存中的新值更新给主存,并且比较和更新的这两个操作是一个原子的整体,这就保证同一时间只有一个线程更新成功。但是有一个问题,假如有一个线程对主存的共享资源进行了更新,但是并没有及时更新主存,那么比较就会出问题,因为CAS并不能够保证可见性。CAS操作需要volatile的支持,所以二者常常配合使用来解决并发线程的安全性问题。
CAS适用于线程数少于CPU核心数的情况。
Java中的13个原子操作类 跳转链接
使用原子的方式更新基本类型,Atomic包提供了以下3个类。
- AtomicBoolean:原子更新布尔类型。
- AtomicInteger:原子更新整型。
- AtomicLong:原子更新长整型。