Java内存模型与线程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fanfan199312/article/details/88079781

1.计算机硬件的效率与一致性

1.1多任务处理的原因 

    在我们开发的过程中,经常听到多cpu,多线程处理任务。这么做的目的1.为了充分利用cpu的计算能力,2.计算机的运算速度与它的存储系统和通信子系统速度的差距太大,磁盘I/O,网络通信或者数据库访问要花费很长时间(相比cpu运算),为了不让cpu资源浪费,我们使用多线程操作,在某个线程与存储系统或者通信系统交互的时候,让出cpu资源,让其他线程去执行自己的运算,充分利用资源。

    另外在并发的应用场景中,TPS(Transactions Per Second)每秒事务处理数:一秒内服务端平均能响应的请求总数,tps值与服务器端程序的并发能力有着非常密切的关系。并发的能力决定了一个网站的高峰时期能承载的用户量。

1.2 操作系统的内存模型

    我们使用多线程来充分利用cpu的计算资源,但是还是不可避免的要进行内存数据资源的访问,内存的访问速度与cpu的计算速度相差几个数量级,所以现代计算机又引入了一层高速缓存来作为内存与处理器之间的缓冲,把需要的数据放入到高速缓存中,等运算结束以后把高速缓存中的数据同步到内存中。(高速缓冲存储器的价格昂贵,速度接近cpu的计算速度),在多cpu中,每个处理器有自己的高速缓存,而他们又共享同一主内存,当多个处理器的运算任务涉及在同一块主内存区域的同一块变量时,就会存在数据不一致的问题。为了解决一致性,就出现了一些缓存一致性的协议,比如MSI、MESI、MOSI、Synapse,Firefly及Dragon,protocol。(与高速缓存类比,Java的每个线程都有自己的工作内存)

放图 (处理器、高速缓存、主内存间的交互关系)

1.3 处理器的乱序执行 

    在处理器内部,为了使运算单元得到充分的利用,处理器对输入的代码进行乱序执行(Out-Of-Order Execution)优化,处理器会在执行以后进行重组,保证代码中各个语句的计算先后顺序,与输入代码中的顺序一致。与处理器的乱序执行优化类似,Java虚拟机的及时编译器也有类似的指令重排序(Instruction Reorder)优化。

2.Java内存模型

    Java虚拟机规范定义了Java内存模型(Java Memory Model)屏蔽掉各种硬件和操作系统的内存访问差异,让Java程序在各个平台下都能达到一致的内存访问效果。Java内存模型的主要目标是定义各个变量(变量包括实例字段,静态字段,构成数组对象的元素这些线程可共享的变量,不包括局部变量与方法参数等线程私有的变量)的访问规则,即在虚拟机中将变量存储到内存,和从内存中取出来的底层细节。

2.1 主内存和工作内存

    Java内存模型规定,所有变量都存储在主内存(类比操作系统硬件主内存),每条线程有自己的工作内存(类比操作系统高速缓存),线程的工作内存保留了主内存变量的副本,线程对变量的访问,读取赋值等只能在工作内存中进行,二不能直接读写主内存中的变量,不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递都需要通过主内存完成。如图:

图(线程、主内存、工作内存三者的交互关系)

2.2 主内存和工作内存的交互操作

lock(锁定):作用于“主内存”的变量,它把一个变量表示为一条线程独占的状态

unlock(解锁):作用于”主内存”的变量,他把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

read(读取):作用于“主内存”的变量,它把一个变量的值从主内存传输到线程的工作内存中

load(载入):作用于“工作内存”的变量,它把read操作读到工作内存的变量值放入工作内存的变量副本中。

use(使用):作用于“工作内存”的变量,当虚拟机执行到某条字节码指令,需要使用变量值时,就会执行这个操作。

assign(赋值):作用于“工作内存”的变量,它将执行引擎收到的值赋值给工作内存的变量,当虚拟机执行到字节码指令,需要赋值时就执行这个操作。

store(存储):作用于“工作内存”的变量,它把工作内存的变量传送到主内存中,以便随后的write操作。

write(写入):作用于“主内存的”的变量,它把store操作从工作内存中传送过来的值,更新到主内存的变量中。

这些指令之间存在着如下规则:

(1)read操作必须在load操作之前执行,store和write操作必须顺序执行。但比不许挨着执行,如 read a ,read b,load a,load b

(2)不允许read和load、store和write操作之一单独出现。

(3)变量在工作内存中改变了(assign了)之后,必须把它同步到主内存中。

(4)不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程工作内存同步回到主内存

(5)一个新的变量只能在主内存产生,不允许在工作内存中直接使用一个未被初始化(load 和assign)的变量

(6)一个变量在一个时刻只能被一个线程锁定lock,但lock操作可以被同一个线程锁定多次,多次lock之后,需要相同次数的unlock操作,变量才会被解锁。

(7)对变量进行lock操作,会清空工作内存中此变量的值,执行引擎重新从主内存load或者assign操作初始化变量的值。

(8)不允许交叉unlock,如果一个变量事先没有被一个线程lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定的值

(9)对一个变量进行unlock之前,必须先把此变量同步回主内存(store,write)

2.3 volatile型变量的特殊规则

 

3.Java与线程

 

本内容为“深入理解Java虚拟机”-周志明著读书笔记

 

没有更多推荐了,返回首页