Java并发编程实战 学习笔记Day8

今天这篇笔记是关于Java内存模型是如何解决可见性和有序性导致的问题。

首先学习一下什么是Java内存模型

导致可见性的原因是缓存,导致有序性的原因是编译优化,那直接禁用掉缓存和编译优化就可以解决了,但是这样我们的程序性能就大打折扣了。合理的方案就是按需禁用以及编译优化。

Java内存模型是个很复杂的规范,可以从不同的视角来解读,站在程序员的视角,本质上可以理解为,Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法,具体来说,这些方法包括volatile、synchronized和final三个关键字,以及六项Happens-Before规则。

使用volatile的困惑

volatile关键字在很多编程语言中都有,它最原始的意义就是禁用CPU缓存。

在Java1.5之前,使用volatile修饰只能解决该修饰变量的内存可见性问题,只能保证这个变量的修饰可以立即被其他线程看到。在1.5版本的时候Java内存模型对volatile语义进行了增强。

Happens-Before规则

1.程序的顺序性规则

这条规则是指在一个线程中,按照程序顺序,前面的操作Happens-Before于后续的任意操作。

在新的内存模型中,volatile周围的普通字段不能随便重排;编译器生成字节码时,会在volatile写操作的前面和后面分别插入内存屏障(StoreStore屏障|volatile写|StoreLoad屏障),其中StoreStore屏障:禁止上面的普通写和下面的volatile写重排序;StoreLoad屏障:防止上面的volatile写与下面可能有的volatile读/写重排序。

2.volatile变量规则

对一个volatile变量的写操作先行发生于后面对这个变量的读操作,这里的"后面"同样是指时间上的先后顺序。

3.传递性

这条规则是指如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。

4.管程中锁的规则

这条规则是指对一个锁的解锁 Happens-Before 于后续对这个锁的加锁。

管程是一种通用的同步原语,在Java中指的就是synchronized,管程中的锁在Java里是隐式实现的。

5.线程start()规则

这条规则是指,主线程启动子线程后,子线程可以看到主线程在启动子线程前的操作。

6.线程join()规则

这条是关于线程等待的。它是指主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作。当然所谓的“看到”,指的是对共享变量的操作。

final

volatile为禁用缓存以及编译优化,而final就是告诉编译器,这个变量生而不变,可以进行任意优化。

在1.5之后,Java内存模型对final类型变量的重排进行了约束,现在只要我们提供正确构造函数没有逸出,就不会出现问题。

逸出 指的是对封装性的破坏.

学习来源:极客时间

猜你喜欢

转载自blog.csdn.net/qq_38862257/article/details/128670339