Halo
发布于 2022-05-13 / 133 阅读 / 0 评论 / 0 点赞

java 内存模型

jvm 内存读写总则

每条线程还有自己的工作内存(Working Memory), 线程的工作内存中保存了被该线程使用的变量的主内存副本, 线程对变量的所有操作(读取, 赋值等)都必须在工作内存中进行, 而不能直接读写主内存中的数据

主内存与工作内存数据交换

  • 一个新的变量只能在主内存中"诞生"
  • 如果要把一个变量从主内存拷贝到工作内存, 那就要按顺序执行read和load操作
  • 如果要把变量从工作内存同步回主内存, 就要按顺序执行store和write操作
  • Java内存模型只要求上述两个操作必须按顺序执行, 但不要求是连续执行

jvm 内存模型规定了三大特性

  • 原子性(Atomicity), lock, unlock, read, load, assign, use, store和write都是原子操作, 提供给用户的只有 synchronized 块之间的操作(lock/unlock)

  • 可见性(Visibility), 可见性就是指当一个线程修改了共享变量的值时, 其他线程能够立即得知这个修改, 提供给用户的有 volatile, synchronized 和 final

  • 有序性(Ordering), 如果在本线程内观察, 所有的操作都是有序的;如果在一个线程中观察另一个线程, 所有的操作都是无序的

jvm 原子性

规定了主内存与工作内存之间具体的交互协议和原子操作

  • lock(锁定):作用于主内存的变量, 它把一个变量标识为一条线程独占的状态.
  • unlock(解锁):作用于主内存的变量, 它把一个处于锁定状态的变量释放出来, 释放后的变量才可以被其他线程锁定.
  • read(读取):作用于主内存的变量, 它把一个变量的值从主内存传输到线程的工作内存中, 以便随后的load动作使用.
  • load(载入):作用于工作内存的变量, 它把read操作从主内存中得到的变量值放入工作内存的变量副本中.
  • use(使用):作用于工作内存的变量, 它把工作内存中一个变量的值传递给执行引擎, 每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作.
  • assign(赋值):作用于工作内存的变量, 它把一个从执行引擎接收的值赋给工作内存的变量, 每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作.
  • store(存储):作用于工作内存的变量, 它把工作内存中一个变量的值传送到主内存中, 以便随后的write操作使用.
  • write(写入):作用于主内存的变量, 它把store操作从工作内存中得到的变量的值放入主内存的变量中.

可见性

volatile 关键字

  • 保证 load/use 连续执行, store/write 连续执行

  • volatile 可以看作是一个轻量版的 synchronized, 比如一个共享变量如果自始至终只被各个线程赋值和读取, 而没有其他操作的话, 那么就可以用 volatile 来代替 synchronized 或者代替原子变量, 足以保证线程安全

  • volatile 属性的读写操作都是无锁的, 正是因为无锁, 所以不需要花费时间在获取锁和释放锁上, 所以说它是高性能的, 比 synchronized 性能更好

有序性

程序次序规则(Program Order Rule)

在一个线程内, 按照控制流顺序, 书写在前面的操作先行发生于书写在后面的操作. 注意, 这里说的是控制流顺序而不是程序代码顺序, 因为要考虑分支, 循环等结构.

管程锁定规则(Monitor Lock Rule)

一个unlock操作先行发生于后面对同一个锁的lock操作. 这里必须强调的是"同一个锁", 而"后面"是指时间上的先后.

volatile变量规则(Volatile Variable Rule)

对一个volatile变量的写操作先行发生于后面对这个变量的读操作, 这里的"后面"同样是指时间上的先后.

线程启动规则(Thread Start Rule)

Thread对象的start()方法先行发生于此线程的每一个动作.

线程终止规则(Thread Termination Rule)、

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

线程中断规则(Thread Interruption Rule)

对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生, 可以通过Thread::interrupted()方法检测到是否有中断发生.

对象终结规则(Finalizer Rule)

一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始.

传递性(Transitivity)

如果操作A先行发生于操作B, 操作B先行发生于操作C, 那就可以得出操作A先行发生于操作C的结论.


评论