并发编程这一块内容,是高级资深工程师必备知识点,25K起若是不懂并发编程,那基本到顶。可是并发编程内容庞杂,如何系统学习?本专题将会系统讲解并发编程的全部知识点,包括但不限于:java
线程通讯机制,深刻JMM内存模型原理,深刻synchronized原理,深刻volatile原理,DCL,详解AQS,CAS,可重入锁,读写锁原理,详解并发工具类,深刻理解threadLocal,Fork、Join,原子类详解,Java并发集合详解(ConcurrentHashMap,ConcurrentLinedQueue,ConcurrentListMap等),阻塞队列深刻探究,深刻线程池原理及其设计思想。 本文为深刻理解java内存模型。程序员
主线如上图红色箭头,你们能够先看看总体讲的是什么。java内存模型前面是铺垫,后面是相关内容。面试
共享变量(实例域,静态域,数组元素)才会用到。 局部变量,方法定义参数等不会在线程间共享,因此他们不会有内存可见性问题,也不受内存模型影响编程
Java内存模型简称JMM(Java Memory Model),是Java虚拟机所定义的一种抽象规范,用来屏蔽不一样硬件和操做系统的内存访问差别,让java程序在各类平台下都能达到一致的内存访问效果。数组
主内存能够简单理解为计算机当中的内存,但又不彻底等同。主内存被全部的线程所共享,对于一个共享变量(好比静态变量,或是堆内存中的实例)来讲,主内存当中存储了它的“本尊”。缓存
本地内存能够简单理解为计算机当中的CPU高速缓存,但又不彻底等同。每个线程拥有本身的工做内存,对于一个共享变量来讲,工做内存当中存储了它的“副本”。为啥有本地内存这个概念?由于直接操做主内存太慢了安全
经过一系列内存读写的操做指令(JVM内存模型共定义了8种内存操做指令,之后会细讲),线程A把静态变量 s=0 从主内存读到工做内存,再把 s=3 的更新结果同步到主内存当中。从单线程的角度来看,这个过程没有任何问题。多线程
理解重排序前这个概念前,咱们先转换场景,从java内存模型走出来,来到硬件CPU这个维度。并发
在执行程序时为了提升性能,编译器和处理器经常会对指令作重排序(简单理解就是本来咱们写的代码指令执行顺序应该是A→B→C,可是如今的CPU都是多核CPU,为了秀下优越,为了提升并行度,为了提升性能等,可能会出现指令顺序变为B→A→C等其余状况)。app
固然CPU们也不是随便就去重排序,须要知足如下两个条件(遵循的规则):
从 Java 源代码到最终实际执行的指令序列,会分别经历下面三种重排序:
那么重排序会遵循什么样的规则?
无论怎么重排序,(单线程)程序的执行结果不能被改变。编译器,runtime和处理器都必须遵照as-if-serial语义。OK,这就至关于给CPU们定下规则。不要随便重排序。要知足我这个as-if-serial的前置条件,才能重排序。
as-if-serial语义把单线程程序保护了起来,遵照as-if-serial语义的编译器,runtime和处理器共同为编写单线程程序的程序员建立了一个幻觉:单线程程序是按程序的顺序来执行的。as-if-serial语义使程序员没必要担忧单线程中重排序的问题干扰他们,也无需担忧内存可见性问题。
注意:as-if-serial只保证单线程环境,多线程环境下无效。那多线程,并发编程下怎么办?
上面的这些重排序均可能致使多线程程序出现内存可见性问题,JMM那么如何解决?
JMM属于语言级的内存模型,它确保在不一样的编译器和不一样的处理器平台之上,经过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。
内存屏障也称为内存栅栏或栅栏指令,是一种屏障指令,它使CPU或编译器对屏障指令以前和以后发出的内存操做执行一个排序约束。
volatile即是基于内存屏障实现的。 **观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令。**这个指令就至关于一个内存屏障。具体表现为:
从而保证了,若是某个线程对volatile修饰的共享变量进行更新,那么其余线程能够立马看到这个更新,这就是所谓的线程可见性。
(注,关于volatile会在后面单章讲解,这里不过于赘婿)
从jdk5开始,java使用新的JSR-133内存模型,基于happens-before的概念来阐述操做之间的内存可见性。 换句话说,在JMM中,若是一个操做执行的结果须要对另外一个操做可见,那么这两个操做之间必须存在happens-before关系。 happens-before原则是JMM中很是重要的原则,它是判断数据是否存在竞争、线程是否安全的主要依据,保证了多线程环境下的可见性。 这个的两个操做既能够在同一个线程,也能够在不一样的两个线程中。
摘录一些happens-before规则以下: 一、程序顺序规则:一个线程中的每一个操做,happens-before于该线程中任意的后续操做。 二、监视器锁规则:对一个锁的解锁操做,happens-before于随后对这个锁的加锁操做。 三、volatile域规则:对一个volatile域的写操做,happens-before于任意线程后续对这个volatile域的读。 四、传递性规则:若是 A happens-before B,且 B happens-before C,那么A happens-before C。 注意:两个操做之间具备happens-before关系,并不意味前一个操做必需要在后一个操做以前执行!仅仅要求前一个操做的执行结果,对于后一个操做是可见的,且前一个操做按顺序排在后一个操做以前。
那么说了那么多规则,来看看happens-before与JMM的关系