3. Java内存模型
1. Java内存模型基础
1. 并发编程两个关键性问题
线程之间如何通信:
通信是指线程之间如何交换数据。通信机制有两种:共享内存和消息传递。
修改共享内存的是隐式通信,而消息传递没有公共状态,为显示传递。
线程之间如何同步:
通信和同步是相同的。
2. Java内存模型抽象结构
Java内存模型要结合JVM来看,线程共享的是堆内存和方法区,私有的是程序计数器,虚拟机栈和本地方法栈。
线程和主存之间有本地内存,存储着共享变量的副本。两个线程通信,就需要将另一个线程更新的数据重新更新到本地内存的副本中。
线程间的通信需要经过主存,线程1修改的值会先存储在本地内存中。当线程1与线程2需要通信,线程1将值写回到主存,然后线程2更新。
3. 指令重排序
- 编译器优化的重排序
- 指令集并行的重排序
- 内存系统的重排序
指令重排序为的是提高CPU的执行效率,volatile会禁止指令重排序。但是volatile只能保证一致性和可见性,对于复杂操作不保证原子性操作(volatile++类似的操作)。
4. happens-before
2. 重排序
编译器和处理器为了优化程序性能而对指令进行的重新排序。
1. 数据依赖行
数据依赖三种形式:读后写、写后写、写后读。
2. as-if-serial语义
不管怎么重排序,程序执行的结果不能被改变。编译器、runtime、处理器必须满足。
3. volatile内存语义
volatile的写锁的释放;volatile的读锁的获取
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量更新到主存中。
4. 锁的内存语义
锁可以让临界区互斥执行,还可以让释放锁的线程向获得同一个锁的线程发送消息。
1. intel中lock命令
- 确保内存中读改写操作原子性执行。
- 禁止该命令与之前,之后的命令进行指令重排序。
- 把写缓冲区中的所有数据刷新到内存中。
Java线程之间的通信有4四种:
- 线程A写volatile变量,线程B读volatile变量
- 线程A写volatile变量,线程B用CAS更新volatile变量
- 线程A用CAS更新volatile变量,线程B读volatile变量
- 线程A用CAS更新volatile变量,线程B用CAS更新volatile变量
2. AQS
AQS是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。
非阻塞数据结构和原子变量类。
下面是concurrent包下实现的示意图
5. final域
1. final域重排序的规则
- 在构造函数内赋值的final与随后把这个对象引用赋值给一个引用变量,这两个操作之间不能重排序。
- 初次读这个final域的对象的引用与初次读这个final域,这两个操作不能重排序。