首页 > 代码库 > Java内存模型
Java内存模型
多线程如何通信,同步?
1)通信: 共享内存/消息队列
2)同步: 访问时加锁/消息队列
JMM采用共享内存以及访问时加锁。JMM决定一个线程对变量的写入何时对另一个线程可见。
Java中,静态变量/实例变量/数组元素都分配在堆中,而局部变量/方法参数/异常变量分配在栈中。线程共享堆,但是独有栈。所以栈不存在共享可见性问题。
每个线程有自己的工作内存,可能是缓存/写缓冲区/寄存器等。线程间通信时,线程A先写到工作内存,再刷新到主内存;然后线程B从主内存读取最新数据到工作内存并创建副本。由于存在重排序,所以结果可能有所不同。
重排序:
1)编译器优化的重排序: 改变代码执行顺序
2)处理器优化的重排序:指令级并行技术(ILP),能并发执行多条指令,改变执行顺序
3)内存重排序: 由于用缓存和读写缓冲区,使得load和store可能乱序执行
JMM的编译器会禁止特定类型的编译器重排序,JMM的处理器通过插入内存屏障(Load/Load, Load/Store, Store/Load, Store/Store)禁止特定类型的处理器重排序。
如果一个操作对另一个操作存在可见性,必须满足happens-before规则。如果两个操作缺乏happens-before关系,JVM可以任意重排序。
happens-before规则:
1)程序规则:之前的代码先于后面的代码执行
2)锁规则: unlock先于lock执行
3)volatile规则: 写入先于读取执行
4)线程启动规则:Thread.start()优于所有后续动作执行
5)线程终止规则: 线程中所有动作必须先于其它线程检测到该线程结束执行
6)线程中断规则: 当一个线程在另一个线程上调用interrupt,必须在被中断线程检测到interrupt之前执行
7)终结器规则: 对象的构造函数必须在启动该对象的终结器之前执行完成。
8)传递规则: A happens-before B, B happens-before C -> A happens-before C
volatile, synchronized, Lock, final能保证内存可见性。volatile变量每次从主内存load数据,synchronized和Lock在开始的时候将内存数据copy到工作内存,结束的时候将工作内存的数据refresh到主内存。final变量一旦初始化完,对其他线程立即可见。
volatile, synchronized能保证有序性。volatile通过插入内存屏障禁止重排序,synchronized通过每次只允许一个线程对一个变量加锁。
Java内存模型