JVM 内存模型(JMM) 三大特性

Published 2021年10月19日 21:59 by james

特性

原子性

原子性:指一个操作是不可中断的,即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰

比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A 给他赋值 1,线程 B 给它赋值为 -1,。那么不管这两个线程以何种方式,何种步调工作,i的值要么是1,要么是-1,线程A和线程B之间是没有干扰的。这就是原子性的一个特点,不可被中断。

原子性表示一步操作执行过程中不允许其他操作的出现,直到该操作的完成。

在多线程环境下,原子性表现在,当前线程执行字节码的过程中不允许切换到其他线程,去执行其他的字节码。

在JMM抽象模型中定义了8中原子操作。

  • a. lock(锁定): 作用于主内存中的变量,将某个变量标识为某个线程的独占状态。
  • b. `unlock(解锁): 作用于主内存中的变量,将某个变量从某个线程的独占状态释放出来,可以被其他线程锁定。
  • c. read(读取): 将主存中的变量从主存中读取到线程的工作内存中,供load操作使用。
  • d. load(载入): 作用于线程工作内存,将read从主存读取的变量,保存到工作内存的变量副本。
  • e. use(使用): 作用于工作内存中的变量,当虚拟机执行到需要变量的字节码时,就会需要该动作。
  • f. assign(赋值): 作用于工作内存中的变量,当虚拟机执行变量的赋值字节码时,将执行该操作,将值赋值给工作内存中的变量。
  • g. store(存储): 作用与工作内存中的变量,将工作内存的变量传递给主存。
  • h. write(写入): 作用于主存的变量,将store步骤中传递过来的变量,写入到主存中。

以上8种操作都是原子性的,JMM内存模型只保证了操作执行的顺序性,但是不保证操作的连续性。

有序性

有序性:对于一个线程的执行代码而言,我们总是习惯地认为代码的执行时从先往后,依次执行的。这样的理解也不能说完全错误,因为就一个线程而言,确实会这样。但是在并发时,程序的执行可能就会出现乱序。给人直观的感觉就是:写在前面的代码,会在后面执行。有序性问题的原因是因为程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。

synchronized修饰的代码只能被被当前线程占用,避免由于其他线程的执行导致的无序行。

volatile关键字包含了禁止指令重排序的语义,使其具有有序性。

指令重排:可以保证串行语义一致,但是没有义务保证多线程间的语义一致,对于提高CPU处理性能是十分重要的

哪些指令不能重排:Happen-Before 规则

  • 程序顺序原则:一个线程内保证语义的串行性
  • volatile规则:volatile 变量的写,先发生于读,这保证了 volatile 变量的可见性
  • 锁规则:解锁(unlock)必然发生在随后的加锁(lock)前
  • 传递性:A先于B,B先于C,那么A必然先于C
  • 线程的start()方法先于它的每一个动作
  • 线程的所有操作先于线程的终结(Thread.join()
  • 线程的中断(interrupt())先于被中断线程的代码
  • 对象的构造函数执行,结束先于 finalize() 方法

可见性

可见性:指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。显然,对于串行程序来说,可见性问题  是不存在。因为你在任何一个操作步骤中修改某个变量,那么在后续的步骤中,读取这个变量的值,一定是修改后的新值。但是这个问题在并行程序中就不见得了。如果一个线程修改了某一个全局变量,那么其他线程未必可以马上知道这个改动。

0 comments

There are no comments yet.

Add a new comment

Similar posts

There are no similar posts yet.