1.Java 内存模型中的可见性、原子性和有序性

1.1 可见性

可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。

可见性问题由JVM缓存优化引起。

1.2 原子性

原子是世界上的最小单位,具有不可分割性。

1.3 有序性

Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。

有序性问题由JVM指令重排序优化引起。

2.保证可见性

写屏障:写屏障保证在该屏障之前的,所有共享变量的修改都同步到主存中。
读屏障:保证在该屏障之后,对共享变量的读取,取的是主存中的最新数据。

3.保证有序性

写屏障:防止指令重排,防止写屏障之前的代码出现在写屏障后面
读屏障:保证在读屏障之后的代码不会排在读屏障前面。
有序性的保证也只是保证了本线程内相关代码不被重排序。

4.volatile不能保证原子性

5.double-checked locking

5.1 举例

5.1.1 问题

在这里插入图片描述

5.1.2 纠正

如果把共享变量INSTANCE完全放在synchronized所保护的代码块之内,就能防止指令重排。

5.1.3 解决

在共享变量INSTANCE上加volatile,解决指令重排序。

6.happens-before规则

happens-before规定了对共享变量的写操作对其他线程的读操作可见,它是可见性与有序性的一套规则总结,抛开以下happens-before规则,JMM并不能保证一个线程对共享变量的写,对于其他线程对该共享变量的读可见

6.0 更新博文中的happends-before

6.1及以后不必看了

感觉下面的不好,所以重新写了
参考:https://www.jianshu.com/p/9464bf340234

1.程序的顺序性规则
在一个线程内一段代码的执行结果是有序的。就是还会指令重排,但是随便它怎么排,结果是按照我们代码的顺序生成的不会变。
2.volatile规则
如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见。
3.传递性规则
即hb(A, B) , hb(B, C),那么hb(A, C)。
4.管程中的锁规则
一个线程对这个锁解锁之后,另一个线程获取了这个锁都能看到前一个线程的操作结果。
(管程是一种通用的同步原语,synchronized就是管程的实现)
5.线程start()规则
主线程A启动线程B,线程B中可以看到主线程启动B之前的操作。
6.线程join()规则
主线程A等待子线程B完成,当子线程B执行完毕后,主线程A可以看到线程B的所有操作。

6.1 线程解锁m之前对变量的写,对于接下来对m加锁的其他线程对该变量的读可见

在这里插入图片描述

6.2 线程对volatile变量的写,对接下来其他线程对该变量的读可见

6.3 线程start前对变量的写,对该线程开始后对该变量的读可见

6.4 线程结束前对变量的写,对其他线程得知它结束后的读可见

在这里插入图片描述

6.5 线程t1打断(interrupt)t2前对变量的写,对于其他线程得知t2被打断后对该变量的读可见(通过t2.interrupted或t2.isInterruped)

在这里插入图片描述

6.6 对变量默认值(0,false,null)的写,对其他线程对该变量的读可见

6.7 具有传递性,如果A happens-before B,且B happens-before C,那么 A happens-before C

/在这里插入图片描述
/-------------------------------------------------------我是一条没有感情的分割线--------------------------------------/
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐