Java精通并发-JMM与happen-before规则深入详解【纯理论】

在之前已经对于Java的volatile关键字的原理作用进行了一个比较详细的学习,这次则来更进一步的去了解关于它更多的理论细节,理论都会比较枯燥,但是有助于更好的去理解相关的知识点。

JMM了解:

JMM,全称为Java Memory Model,Java内存模型,它是属于Java语言规范的一个内容,在一个多核的场景下,某一个处理器对某一个变量进行了一个修改操作,这些修改肯定是能被其它处理器所探测获取到的,但是什么时候能获取到协程中是木有明确规定的。比如说是修改了立马就能获取到?还是说得等一段时间待缓存失效了才能被其它核心的处理器所看到?很多时候可能并非实时的能获取到修改的值的,所以对于底层的硬件系统就需要解决这样的一个问题,其实这个问题就是上一次学volatile中所提及的变量的可见性:

除了这个可见性问题之外,还有一个顺序性问题, 比如说某处理器核心连续修改了2个变量的值,比如先修改a,再修改了b,那这俩变量都被修改之后对于其它核心处理器看到的也是先是a再了b还是说先是b再是a。用来解决这样的问题的一个重要依据就称之为JMM,它其实是一个规范,具体的实现则是由不同的JVM来决定的,所以很显然不同架构的处理器其实现也会是不一样的。对于JMM主要是定义了如下几个问题:

1、变量的原子性问题。

对于实例或静态变量来说,哪些是具备原子性的,哪些是不具备原子性的,根据之前咱们学习volatile关键字能知道对于long和double它不是原子性的,而其它原生数据类型则是原子性的。

2、变量的可见性问题。

这个已经在上面提及了,也就是当一个处理器的核心修改了一个变量的值,对于其它核心来说何时能看到。

3、变量修改的时序性问题。 

这个也已经在上面提及了, 也就是对于处理器修改了若干个变量,对于其它处理器而言看到的顺序是咋样的。

happen-before规则:

而对于上面JMM中所提到的可见性和时序性问题而言,最终都涉及到happen-before规则了,全新的概念,以前完全木有听说过。。啥是happen-before规则呢?简单描述的话可以这样理解,假设“A happen-before B”,则说明A一定是在B之前发生的。而对于happen-before它是具有一定的特点的,并非所有的代码都具有此特性,下面看一个非常之简单的代码,来猜一下它是否具有happen-before特点?

比如一个线程调用getX()方法,另一个线程调用setX()方法,其实这个程序是不具备happen-before规则的,在多线程环境下有可能读的值是一个旧值,要想搞清楚为啥?则就需要了解它都有哪些规则:

1、顺序执行规则(限定在单个线程上的):该线程的每个动作都happen-before它的后面的动作。

这个比较好理解,单线程执行就是串行化的执行嘛,有啥好说的,这里不是刚好在学习happen-before规则嘛,所以用它来理解一下顺序的问题。那是不是有了这个规则对于之前所提到的JVM会指令重排应该不会发生了呀?这里要特别说明,happen-before并不会限定指令重排序的,这里需要这样理解:happen-before的出现跟指令重排序并不是一个矛盾的关系,如果出现了指令重排序最终还是符合happen-before的规则的话那指令重排序是不会被限制的。比如一个线程去执行A、B、C三条语句,A肯定是happen-before B的,但是!!!并不表示A一定会在B之前执行,假如A和B木有任何关系是存在JVM重排序的可能的,而如果A和B之间有关系那么JVM重排序时肯定是不会将应该的顺序进行颠倒的,所以happen-before跟指令重排序其实并不是矛盾的。

2、隐式锁(monitor)规则:unlock happen-before lock,之前的线程对于同步代码块的所有执行结果对于后续获取锁的线程来说都是可见的。

这个也就是平常我们说的同步锁,对于同一把锁,一个线程的锁释(unlock)放动作一定是在另一个线程尝试获取锁(lock)动作之前的,针对这个规则,实际的结果就会是:有两个线程A和B,A线程已经获取到锁开始执行同步块中的代码了,接下A unlock,接着B开始lock,那么A一定是happen-before B的,而对于A所执行代码的结果对于B线程而言一定都是可见的。哦,原来我们平常的同步锁的现象居然背后还有一套这个理论做为支撑呢~~所以这也是理论对于知识它是有促进作用的。

3、volatile读写规则:对于一个volatile变量的写操作一定是happen-before后续对该变量的读操作。

用这条规则,其实就能说明,当我们修改一个volatile变量的值之后,其它的线程获取该变量的值一定是最新的,因为写一定是在读之前!!

4、多线程的启动规则:Thread对象的start方法happen-before该线程run方法中的任何一个动作。包括在其中启动的任何子线程。

我们知道在一个线程中是可以启动另一个子线程的,如果在run方法中开启了一个新的线程,对于新的线程来说,它在执行run中的业务逻辑的时候是一定可以看到启动它的父线程在执行start()之前的所有操作的结果的。换言之父线程启动线程之后,父线程所做的一些操作对于子线程都是可见的。

5、多线程的终止规则:一个线程启动了一个子线程,并且调用了子线程的join方法等待其结束,那么当子线程结束后,父线程的接下来的所有操作都可以看到子线程run方法中的执行结果。

这个比较好理解,join()本来就是等待子线程执行完,只是平常木知道有这套理论而已。

6、线程的中断规则:可以调用interrupt方法来中断线程。这个调用happen-before对该线程中断的检查(isInterrupted)。

这条规则的意思是如果一个线程被另一个线程给interrupt的话,那么后续再调用线程的isInterrupted()时是一定能够检测中断状态的。

以上就是整个关于happen-before规则的学习,虽说非常之抽象,但是这种理论的掌握对于平常线程的使用会更加的踏实,意义还是相当大的。 

关注个人公众号,获得实时推送

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webor2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值