深入理解jvm--java内存模型

提纲

Java内存模型

设计规则:这个模型必须定义的足够严谨,才能让java 的并发访问操作不会产生歧义,也必须定义的足够宽松,使虚拟机有足够的自由空间去利用硬件的各种特性来获取更好的执行速度。

主内存与工作内存

Java内存模型的主要目标时定义程序中各个变量的访问规则,即在虚拟机中将变量(不包括局部变量和方法参数)存储到内存和从内存中取出变量这样的底层细节。

Java内存模型规定了所有变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了被线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。线程之间变量的传递均需要通过主内存来完成。

内存间交互操作

Java内存模型中定义了以下操作都是原子的、不可再分的:

lock:作用于主内存,把一个变量标识为一条线程独占的状态。

unlock:作用于主内存,把一个处于锁定状态的变量释放出来。

read:作用于主内存,把一个变量的值从主内存传输到线程的工作内存中。

load:作用于工作内存,把read操作得到的变量值放入到工作内存的变量副本中。

use:作用于工作内存,把工作内存的变量的值传递给执行引擎。

assign:作用于工作内存,把一个从执行引擎接收到的值赋给工作内存的变量。

store:作用于工作内存,把工作内存中一个变量的值传递到主内存中。

write:作用与主内存,把从工作内存中得到的变量的值放入主内存的变量中。

执行基本操作的规则:

l  不允许read和load、store和write单一出现

l  变量在工作内存中改变了之后必须把改变化同步回主内存

l  不允许线程assign操作把数据同步回主内存中。

l  一个变量在同一时刻只允许同一条线程对其进行lock操作。但lock可以被同一条线程执行多次。

l  不允许在工作内存中使用一个为被初始化的变量。

l  没有被lock的变量不允许使用unlock操作

l  lock后会清工作内存中此变量的值,需要重新执行load或assign操作初始化变量的值

l  执行unlock前必须先把变量同步回主内存中

对于volatile变量的特殊规则

volatile变量的特性:

1. 对所有线程的可见性

2. 禁止指令重排序优化

可见性是指当一条线程修改了这个变量的值,新值对于其它变量来说是可见的。

volatile在不满足以下两条规则的运算场景中,依然要通过加锁来保证原子性:
  1. 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。

  2. 变量不需要与其它状态变量共同参与不变约束。

从硬件架构上来讲,指令重排序是指CPU采用了允许将多条指令不按规定的顺序分开发送给各相应的电路单元处理。

volatile可以禁止指令重排序优化是因为其多了一道寄存器加0的内存屏障,并且通过lock指令将本CPU的cache写入了内存,会令别的CPU或内核无效化其Cache

Java内存模型对volatile变量定义的特殊规则:

定义变量V、W被volatile修饰,线程T会操作变量V和W。

每次使用前从主内存读取 
read、load、use必须顺序整体出现。前一个操作是load时才能use,后一个操作时use时才能load。 
每次修改后立即同步回主内存 
assign、store、write必须顺序整体出现。前一个操作是assign时才能store,后一个操作时store时才能assign。 
避免指令重排序 
如果T对V的use或者assign先于T对W的use或者assign,那么T对V的load或者write必须先于T对W的load或者assign。

对于long和double型变量的特殊规则

long和double的非原子协定:

允许虚拟机将没有被volatile修饰的64位数据类型的读写操作划分为两次32位操作来进行。

原子性、可见性与有序性

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

  synchronized块之间的操作具备原子性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

  volatile保证了多线程操作的可见性。synchronized保证了同步块的可见性。final通过构造器初始化完成,也具有可见性。

有序性:即程序执行的顺序按照代码的先后顺序执行。

  volatile禁止指令重排序,synchronized也具有有序性。

先行发生原则

①程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作,准确的说是控制流顺序。

②锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作

③volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作

④传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C

⑤线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作

⑥线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生

⑦线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行

⑧对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

Java与线程

线程的实现方式

使用内核线程实现

使用用户线程实现

使用用户线程加轻量级进程混合实现

Java线程调度

线程调度方式有两种:协同式调度和抢占式调度。

可以通过设置线程优先级为线程分配时间。

状态转换

新建:创建后尚未启动的线程

运行:处于此状态的线程可能正在执行,也可能正在等待着CPU分配执行时间

无限期等待:线程不会被分配CPU执行时间。需要被其他线程显示的唤醒。以下方法会进入此状态:

     没有设置timeout参数的Object.wat()方法

     没有设置timeout参数的Thread.join()方法

     LockSupport.park()方法

限期等待:线程不会被分配CPU执行时间,不需要被其他线程显示的唤醒。系统会在一定时间内自动唤醒。以下方法会进入此状态:

     Thread.sleep()方法

     设置了timeout参数的Object.wat()方法

     设置了timeout参数的Thread.join()方法

     LockSupport.parkNanos()方法

     LockSupport.parkUntil()方法

阻塞:等待获取一个排他锁,将在另一个线程放弃这个锁的时候发生。

结束:线程结束执行。


猜你喜欢

转载自blog.csdn.net/yinweicheng/article/details/80918303