并发编程的三大特性
原子、可见、有序
讲一下 JMM(Java 内存模型) 。volatile 关键字解决了什么问题?
JMM这个点比较大。JMM概念及并发编程特性
- Java为了保证并发编程中可以满足原子性、可见性及有序性,定义了JMM。JMM内存模型定义了共享内存系统 中多线程程序读写操作行为的规范。
- 而且这个JMM规范保证Java程序在各种平台下对内存及cache的访问的一致性。(解释了为什么不复用操作系统的内存模型,因为java跨平台)
- (JMM内存结构图)
-
- 首先线程的共享变量在主内存,但是每个线程都有私有的本地内存,本地内存存的是共享变量的副本(本地内存是抽象的,可能是高速cache等),
- 定义了一个共享变量什么时候写入,共享变量什么时候对另一个线程可见。
- (操作规则)
-
- 线程的操作都只能在本地内存进行,不能直接操作主内存。
- 不同线程不能直接 访问其他线程的本地内存,变量需要通过主内存传递。
- 然后 为了解决可见性,JMM遵循了happens-before规则。然后讲happens-before规则。
volatile 关键字解决了什么问题?讲讲volatile
volatile 保证 可见性 和 有序性。
可见性:(两句话)
- 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量副本 ( 不仅仅是volatile变量,普通变量也会 ) 值立即刷新回主内存中。 (volatile写后 有写屏障)
- 当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效(所有共享变量的本地副本都无效,不仅仅是volatile变量),重新回到主内存中读取最新共享变量的值(在读volatile之前加读屏障)
为什么不保证原子性
上面说了,读volatile都是读最新值,但是并没有加锁,当两个线程同时读取volatile并修改时,讲修改的值写回主内存存在写覆盖问题。(主要还是读写时没有加锁)
有序性
- 禁止指令重排。
happens-before 原则,它解决了可见性,和顺序写
- JMM解决可见性使用了synchronized 和 volatile, 它们都遵循 happens-before 规则。
- 这个规则 约束了编译器 的优化行为,允许优化,但要遵循happens-before规则。
- 规则是什么,简单来说是 如果一个操作执行的 结果需要 对另一个 操作可见 ,那么这两个操作之间必须要存在happens-before的关系(不管是不是在一个线程中)
- 具体的规则记4-5条:顺序性规则,锁规则,volatile变量规则,传递性规则。