首页 > 代码库 > Java并发拾遗(一)——并发、JMM与重排序

Java并发拾遗(一)——并发、JMM与重排序

一、并发中的关键问题及其解决思路

并发中的关键问题:
1. 线程之间如何通信 —— 线程之间如何交换信息
2. 线程之间如何同步 —— 控制线程的相对执行顺序

两种解决思路:

1. 隐式通信,显示同步 —— 线程之间通过共享内存中的公共状态来隐式通信,那么就必须显示的指定线程见的互斥来实现同步
2. 显式通信,隐式同步 —— 线程之间无公共状态,通过明确的发送消息进行通信,那么由于消息的发送在消息接收之前,就可以实现隐式的同步

Java选择了共享内存的方式来解决并发中的两个关键问题,因此Java中线程的通信是隐式的,对程序员透明,但需要程序员自己来控制线程的同步,如使用互斥锁等。

二、JMM

JMM(Java memory model),控制Java线程之间的通信,定义了线程私有的本地内存和共享的主内存之间的关系,决定了一个线程对共享变量的写入何时对另一个线程可见。

技术分享

如上图,Java不同线程的通信必须经过主内存,JMM通过控制主内存与每个线程的本地内存的交互,来向程序员提供了内存可见性。

三、重排序

在编译或执行时,编译器或处理器常常会对指令进行重排序,有以下三种情况:

1. 编译器重排序:Java编译器通过对Java代码语义的理解,根据优化规则对代码指令进行重排序。

2. 机器指令级别的重排序:现代处理器很聪明,能够自主判断和变更机器指令的执行顺序。

3. 内存系统的重排序:由于处理器与主内存之间存在缓冲,导致在加载和存储操作可能在乱序执行。

其中,1属于由Java来指定规则的编译器重排序,2和3属于处理器级别的重排序。这些重排序就可能导致并发程序出现一些奇怪的问题,比如——可见性问题。

既然重排序导致了问题,那就在一些关键点,禁止重排序,就可以避免这些问题了。首先是编译期的禁止重排序,由于编译期的重排序规则就是由Java编译期来制定的,那么Java肯定能做到这种禁止。而在运行期的处理器级别的禁止重排序,就需要通过插入特定类型的内存屏障指令(memory barries / memory fence)来禁止重排序。JMM就是通过这两个层面的禁止重排序保证了一致的内存可见性。

 

Java并发拾遗(一)——并发、JMM与重排序