深刻理解java虚拟机【并发编程缓存】

  随着多核CPU的高速发展,为了充分利用硬件的计算资源,操做系统的并发多任务功能正变得愈来愈重要,可是CPU在进行计算时,还须要从内存读取输出,并 将计算结果存放到内存中,然而因为CPU的运算速度比内存高几个数量级,CPU内的寄存器数量和容量有限,为了避免让CPU长时间处于等待内存的空闲状态, 在CPU和内存之间引入了速度接近CPU的高速缓存Cache做为CPU和内存之间的缓冲。计算机硬件并发的原理以下:

java

Java虚拟机对并发的支持相似于计算机硬件,java虚拟机的并发支持是经过java虚拟机的内存模型来实现的。Java虚拟机的内存模型分为主内存和 工做内存,程序中全部的变量都存储在主内存中,每一个线程有本身的私有工做内存,工做内存中保存了被该线程使用到的变量的主内存拷贝,线程对变量的全部操做 (读取、赋值等)都必须在工做内存中进行,而不能直接读写主内存中的变量,不一样线程之间也没法直接访问对方工做内存中的变量,线程间变量值的传递须要经过 主内存来完成。Java虚拟机并发原理以下:缓存

  Java虚拟机内存模型中定义了8种关于主内存和工做内存的交互协议操做:安全

(1).lock锁定:做用于主内存的变量,把一个变量标识为一条线程独占状态。并发

(2).unlock解锁:做用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量能够被其余线程锁定。优化

(3).read读取:做用于主内的变量,把一个变量的值从主内存传输到线程的工做内存中,以便随后的load动做使用。spa

(4).load加载:做用于工做内存的变量,把read读取操做从主内存中获得的变量值放入工做内存的变量拷贝中。操作系统

(5).use使用:做用于工做内存的变量,把工做内存中一个变量的值传递给java虚拟机执行引擎,每当虚拟机遇到一个须要使用到变量值的字节码指令时将会执行该操做。线程

(6).assign赋值:做用于工做内存变量,把一个从执行引擎接收到的变量的值赋值给工做变量,每当虚拟机遇到一个给变量赋值的字节码时将会执行该操做。排序

(7).store存储:做用于工做内存的变量,把工做内存中一个变量的值传送到主内存中,以便随后的write操做使用。内存

(8).write写入:做用于主内存的变量,把store操做从工做内存中获得的变量值放入主内存的变量中。


  Java内存模型对上述8种操做有以下的约束:

(1).把一个变量从主内存复制到工做内存中必须顺序执行read读入操做和load载入操做。

把一个变量从工做内存同步回主内存中必须顺序执行store存储操做和write写入操做。

read和load操做之间、store和write操做之间能够插入其余指令,可是read和load操做、store和write操做必需要按顺序执行,即不容许read和load、store和write操做之一单独出现。

(2).不容许一个线程丢弃它的最近的assign赋值操做,即工做内存变量值改变以后必须同步回主内存。只有发生过assign赋值操做的变量才须要从工做内存同步回主内存。

(3).一个新变量只能在主内存中产生,不容许在工做内存中直接使用一个未被初始化(load或assign)的变量,即一个变量在进行use和store操做以前,必须先执行过assgin和load操做。

(4).一个变量在同一时刻只容许一条线程对其进行lock锁定操做,可是lock锁定能够被一条线程重复执行屡次,屡次执行lock以后,只有执行相同次数的unlock操做变量才会被解锁。

(5).若是对一个变量执行lock锁定操做,将会清空工做内存中该变量的值,在执行引擎使用这个变量前,须要从新执行load或assign操做初始化变量的值。

(6).若是一个变量事先没有被lock锁定,则不容许对这个变量进行unlock解锁操做,也不容许对一个被别的线程锁定的变量进行unlock解锁。

(7).一个变量进行unlock解锁操做以前,必须先把此变量同步回主内存中(执行store和write操做)。


  Java中的关键字volatile是java虚拟机提供的最轻量级的线程同步机制,当一个变量被声明为volatile以后,该变量将具有如下两种特性:

(1).volatile保证变量对全部线程的可见性,即任何一个线程修改了该变量的值以后,新值对于全部其余线程都是能够当即得知的。

而普通变量须要先将工做内存中的变量同步回主内存,其余线程都须要从主内存从新读取变量的值才能使用最新修改后的值。

volatile变量也能够在各个工做内存中存在不一致的状况,但因为每次使用以前都须要先刷新(工做内存变量从新执行初始化),执行引擎看不到变量不一致的状况,所以能够任务volatile变量不存在不一致的状况。

可是java中的运算并不是所有都是原子操做,所以volatile变量的运行在并发下同样是线程不安全的。

因为volatile变量只能保证可见性,只有在符合以下两条规则状况才是线程安全的。

a.运算结果不依赖变量的当前值,或者可以确保只有单一线程修改变量的值。

b.变量不须要与其余其余变量共同参与不变约束。

不符合上述两条规则状况下,仍然须要经过synchronized同步关键字或者加锁机制来保证线程安全。

(2).volatile禁止指令重排序优化。

普通变量仅能保证在方法执行过程当中全部依赖赋值结果的地方都能获取正确的结果,而没法保证变量赋值操做顺序与程序代码执行顺序一致。

volatile禁止指令重排序,所以volatile变量的约束以下:

a.volatile变量的操做必须按read->load->use顺序,即每次在工做内存中使用变量前必须先从主内存中刷新最新的值,以保证能看到其余线程对变量的最新修改。

b. volatile变量的操做必须按assign->store->write顺序,即每次在工做内存为变量赋值以后必须将变量的值同步回主内存,以保证让其余线程能看到变量的最新修改。

c.若线程对volatile变量A的assign或者use操做先于对volatile变量B的assign或者use操做,则线程对volatile 变量A的read/load或者store/write操做也一定先于对volatile变量B的read/load或者store/write操做。

相关文章
相关标签/搜索