Java 并发编程:多线程并发内存模型

多任务处理在现代计算机操做系统中几乎已经是一项必备的功能了。在许多状况下,让计算机同时去作几件事情,不只是由于计算机的运算能力强大了,还有一个很重要的缘由是计算机的运算速度与它的存储和通讯子系统速度的差距太大,大量的时间都花费在磁盘I/O、网络通讯或者数据库访问上。若是不但愿处理器在大部分时间里都处于等待其余资源的状态,就必须使用一些手段去把处理器的运算能力“压榨”出来,不然就会形成很大的浪费,而让计算机同时处理几项任务则是最容易想到、也被证实是很是有效的“压榨”手段数据库

 关于可见性

在多核多线程环境中,为了提高运行时数据访问性能,常常会使用多层缓存策略。这种架构也就带来了共享变量可见性问题,每一个核或线程都有本身的本地副本,对于共享变量的读写操做可能不会被其它核或线程知晓,也就是不可见了。缓存

 

 Java内存模型

Java的世界也有属于它本身的内存模型,Java内存模型(Java Memory Model),简称JMM。因为Java被定义成一种跨平台的语言,因此在内存的描述上面也要能是跨平台的,Java虚拟机试图定义一种统一的内存模型,能将各类底层硬件及操做系统的内存访问差别进行封装,使Java程序在不一样硬件及操做系统上都能达到相同的并发效果。它描述了程序中各个变量之间的关系,包括实例域、静态域、数据元素及在实际计算机系统中将变量存储到内存和从内存中取出变量的底层细节。  网络

为更好理解JMM的工做机制,咱们经过下图进行理解。从总体上看有几个比较重要的概念:主内存、工做(本地)内存、共享变量、共享变量副本、线程等。首先看主内存与工做内存及他们的关系,主内存保存了Java程序的全部变量,固然这个变量不包括局部变量和方法参数。工做内存则包含了这些共享变量的副本。其次是线程与工做内存的关系,每一个线程都有一个属于本身的工做内存,不一样线程之间的工做内存是互相不可见的,且线程对变量的操做也只能是针对本身的工做内存。最后是关于线程之间的通讯机制,线程之间不可直接传递。假如某个线程对一个变量进行了从新赋值,那么该如何让另外一个条线程知道呢?线程A将变量改变反应到主存中,线程B再从主存中读取,这样才能完成线程之间的通讯。多线程

 

 JMM主要操做

JMM定义了八个主要的内存操做来完成工做内存与主存的通讯。假如一条线程准备对一个变量进行新的赋值操做,它可能会先用lock操做锁住主内存中的某个变量,不让其余线程得到此变量的锁,直至使用unlock操做释放该变量的锁。接着使用read操做将变量从主存读到工做内存中,紧接着load操做将获得的变量值放到工做内存中的变量副本。use操做则将变量值传给线程执行引擎进行运算操做,assign操做把新的变量值从线程执行引擎中传递到工做内存。store操做则把变量值从工做内存传送到主存中,接着write操做将获得的值写入主存相应的变量中,最后使用unlock操做释放变量的锁。架构

 

 JMM可见性

在Java内存模型中,若是一个线程更改了共享变量的值,其余线程能立刻知道这个更改,则咱们说这个变量具备可见性。通常来讲有四种方式能保证变量的可见性,分别为volatile、synchronized、final和锁。  并发

首先谈谈volatile,被此关键词声明的变量,每当有任何更改时都将当即同步到主存中,而每一个线程要使用这个变量时都要从新从主存刷新到工做内存,这样就确保了变量的可见性。固然,普通变量最终也会同步到主存,再由主存同步到每一个线程的工做内存,只是这个最终可能比较“长久”,不能保证可见性。  性能

其次,关于synchronized,因为synchronized底层也是经过锁进行实现,因此synchronized和锁的本质是同样的。当一个线程释放一个锁时,将会强制刷新工做内存中的变量值到主存中。而当另外一个线程获取此锁的时候将会强制从新装载此变量值。固然这两个线程获取的是同一个锁,这样就保证了变量的可见性。  优化

最后,被final声明的变量一旦完成初始化,其余线程就能看到这个final变量。其实,可见性其实能够当作是一种机制,线程在进入/退出同步块程序时,它将发送/接收一个变量的更改。spa

 JMM有序性

有序性指在线程内看方法的执行,全部的指令都是有序的,都按照一种串行方式执行。而在线程内观察其余线程,全部指令都是无序的,指令均可能交叉执行。Java中提供了volatile和synchronized两个关键词保证线程之间操做的有序性,而这个有序性仅仅是相对的,volatile禁止指令重排序,synchronized则保证持有同一个锁的同步块只能串行运行。操作系统

 JMM原子性

Java内存模型保证了read、load、assign、use、store、write等操做具备原子性,咱们能够认为除了long和double类型外,对其余基本数据类型所对应的内存单元的访问读写都是原子的。但因为这个原子性的颗粒度过小,一般状况下咱们须要更大颗粒度的原子性,这时就须要用锁来保证了。

 

 总结

JMM能够说是Java的基础,也是Java多线程的基础,它的定义将直接影响JVM及Java多线程实现的机制。要想深刻了解多线程并发中的相关问题现象,对Java内存模型的深刻研究是必不可少的。它的定义必须考虑下面几个方面,其一是如何更加有效地提升线程的性能效率;其二是如何将底层物理硬件及操做系统的差别屏蔽掉提供统一的对外概念;最后是如何使它的模型既严谨又宽松,保证语义不会产生歧义和一些优化扩展。