并发——在操做系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行——源自百度百科html
在并发编程中,咱们须要处理两个关键问题:线程之间如何通讯和线程之间如何同步,后续篇章将围绕这两个问题进行介绍。java
本文主要介绍java的通讯机制,刚介绍常见通讯机制主要包括如下两种方式:程序员
Java的并发采用的是共享内存模型,Java线程之间的通讯老是隐式进行,整个通讯过程对程序员彻底透明。在java中,全部实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享。编程
Java线程之间的通讯由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入什么时候对另外一个线程可见。数组
JMM(Java Memory Model)是JVM规范中定义的一种Java内存模型,它的目的是屏蔽掉各类硬件和操做系统的内存访问差别,以实现让Java程序在各类平台上到能达到一致的内存访问效果。 Java内存模型的主要定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节。首先简单说明几个经常使用名称定义:缓存
线程、主内存和工做内存的交互关系如上图所示,和CPU-缓存-内存很相似。 不一样线程之间没法直接访问对方工做内存中的变量,线程间变量值的传递均须要在主内存来完成。 最后注意,为了得到较好的执行性能,Java内存模型并无限制执行引擎使用处理器的寄存器或者高速缓存来提高指令执行速度,也没有限制编译器对指令进行重排序。也就是说,在java内存模型中,也会存在缓存一致性问题和指令重排序的问题。安全
关于主内存与工做内存之间的具体交互协议,即一个变量如何从主内存拷贝到工做内存、如何从工做内存同步到主内存之间的实现细节,Java内存模型定义了如下八种操做来完成:多线程
因此变量读写包含如下几个步骤:并发
注意,Java内存模型只要求上述操做必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是能够插入其余指令的。 除了定义以上8中原子操做,Java内存模型还规定了上述8种基本操做在执行时必须知足必定的操做规则,例如如不容许read和load单独出现(即不容许一个变量从主内存中读取但工做内存不接受),不容许store和write单独出现(即不容许从工做内存中发起了回写单主内存不接受),这里不一一列举,详细网上搜索便可。 Java内存模型还定义了volatile型变量的特殊规则(下一节介绍),以上三种规定共同肯定了Java中哪些内存访问操做是安全的即:jvm
8种原子操做+操做规则+volatile规定=Java中哪些内存访问操做是安全的
当一个变量被定义为volatile后,将具有两种特性:
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其余线程可以当即看获得修改的值。 普通的共享变量不能保证可见性,由于普通共享变量被修改以后,何时被写入主存是不肯定的,当其余线程去读取时,此时内存中可能仍是原来的旧值,所以没法保证可见性。 可是,须要注意的是volatile变量只保证可见性,可是java里面的运算并不是所有都是原子操做例如++操做,这样一样致使volatile修饰变量java运算不安全。 通常不符合如下两条规则的运算场景中,咱们须要经过加锁(synchronized或并发包中的锁)保证变量原子性:
常见的volatile修饰变量的场景是用来做为开关控制并发:
重排序:是指“编译器和处理器”为了提升性能,而在程序执行时会对程序进行的重排序。大体能够分为如下三类:
下面咱们看下jvm如何实现volatile禁止指令重排序的:
最后,关于volatile禁止重排序几点使用说明:
总的来讲JMM内存模型是围绕着在并发过程当中如何处理原子性、可见性和有序性三个特征来创建的。下面就三个特征分别说明:
原子性:即一个操做或者多个操做 要么所有执行而且执行的过程不会被任何因素打断,要么就都不执行。 java内存模型的read、load、assign、use、store和write六个操做直接保证原子性,咱们能够任务基本数据类型访问读写是具备原子性(特殊说明long double64位操做根据jvm实现有关)。 若是场景中须要大范围的原子性保证,java内存模型提供了lock和unlock操做来知足,对应到java代码关键字便是——synchronized。
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其余线程可以当即看获得修改的值。 除了上面介绍的volatile外,java还提供了两个关键字实现可见性,synchronized和final。
有序性:即程序执行的顺序按照代码的前后顺序执行。 java中自然有序性能够总结为一句话:若是在本线程内观察,全部的操做都是有序的;若是在一个线程中观察另一个线程,全部操做都是无序的。前半句是指“线程内表现为串行语义”,后半句表示“指令重排”和“工做内存与主内存同步延迟”现象。 java提供了volatile和synchronized两个关键字来保证线程之间操做的有序性,这里synchronized则是有“同一时刻只容许一条线程对其进行lock操做”这条操做规定获取的,这个规则决定了同一个锁的两个同步块只能串行进入。
最后,能够发现synchronized关键字能够同时解决上述三个问题,固然这个须要付出代价就是性能问题。
《深刻理解java虚拟机》——周志明 www.cnblogs.com/dolphin0520…