关于jmm的一些拙见(1)

大家都知道, 在并发编程中,我们需要处理两个关键问题:(1)线程之间如何通信。(2)线程之间如何同步。

通信是指线程之间以何种方式来交换信息。在命令式编程中,线程之间的通信主要有两种:共享内存和消息传递

在共享内存的并发模型中,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来进行隐式通信。在消息传递的并发模型里,线程之间需要通过明确的发送消息来显式进行通信。

同步是指程序用于控制不同线程之间操作发送相对顺序的机制。 在共享内存并发模型中,程序员必须显式的指定某个方法或某个代码需要在线程之间互斥执行来达到同步的目的。而在消息传递模型中,由于消息的发送必须在消息的接受之前,因此同步是隐式进行的。

java的并发采用的是共享内存模型。java线程之间的通信由java内存模型(JMM)控制,jmm决定一个线程对共享变量的写入何时对另一个线程可见。JMM 定义了线程和主内存之间的抽象关系: 线程之间的共享变量存储在主内存中,每个线程都有一个私有内存(称为本地内存)。在本地内存中存储了共享变量的副本。应当注意的是,本地内存是一个抽象概念,并不真实存在。如图所示为JMM的示意图:

从图中可以看出,线程A与线程B如要通信的话,需经历如下两个步骤:

1. 线程A要把本地内存A中更新过的共享变量副本刷新到主内存中去;

2.线程B要到主内存中读取已经更新之后的变量。

例如,如果本地内存A 和本地内存B 都存有主内存中X的副本(如X都为0)。当A线程对自己本地内存中X的值进行写操作的时候,例如将X置为1. 那么如果要让线程B知道X已经发生改变。那么首先A线程必须把已经更新过的X的值刷新到主内存中去,然后线程B到主内存中重新读取X的值,并刷新自己本地内存X的值。那么此时,B 的本地内存中的值也为1了。JMM 通过控制主内存与每个线程的本地内存之间的交互,来提供内存可见性的保证。

在JMM中, 如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happen-before关系。

happen-before原则如下:

1. 程序顺序规则:一个线程中的每个操作,happen-before与该线程中的任意后续操作;

2.监视器规则:对一个监视器的解锁,happen-before于随后对这个监视器的加锁;

3.volatile变量规则:对一个volatile域的写,happen-before于任意后续对象对这个volatile域的读;

4。传递性:如果A hb B,B hb C, 那么A hb C。

这里需要注意的是,两个操作之间具有hb关系,并不意味着前一个操作必须要在后一个操作之前执行。 而是要求前一个操作执行的结果要对后一个操作可见,且前一个操作顺序排在第二个操作之前(the first is visible to and ordered before the second)

数据依赖性:如果两个操作访问同一个变量,且这两个操作中一个为写操作。那么这两个操作之间就存在数据依赖性。如果重排序两个具有数据依赖关系的操作,那么程序的执行结果就会发生改变。

int a = 3; //A
int b = 4; //B
int c = a+b;//C

上述代码,根据数据依赖性,A和C之间存在数据依赖性,B和C之间也存在数据依赖性。因此,在最终的执行序列中,C不能被重排序到A和B的前面。但A和B之间没有数据依赖性,所以重排序A和B的操作顺序,不会对程序结果发生改变。

根据程序顺序规则的hb原则。 A hb B,B hb C,A hb C;这里A hb B, 但实际执行时B 却可以排在A之前执行。所得到的结果是一样的。那么JMM 会认为这种重排序并不非法,JMM 允许这种重排序。


在计算机中,软件技术和硬件技术都希望 在不改变程序执行结果的前提下,尽可能的提高开发的并行度。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值