【JUC并发】JMM和volatile常见问题

14 阅读3分钟

并发编程的三大特性

原子、可见、有序

讲一下 JMM(Java 内存模型) 。volatile 关键字解决了什么问题?

JMM概念及并发编程特性

JMM这个点比较大。JMM概念及并发编程特性

  1. Java为了保证并发编程中可以满足原子性、可见性及有序性,定义了JMM。JMM内存模型定义共享内存系统 中多线程程序读写操作行为的规范。
  2. 而且这个JMM规范保证Java程序在各种平台下对内存及cache的访问的一致性。(解释了为什么不复用操作系统的内存模型,因为java跨平台)
  3. (JMM内存结构图)
    • 首先线程的共享变量在主内存,但是每个线程都有私有的本地内存,本地内存存的是共享变量的副本(本地内存是抽象的,可能是高速cache等)
    • 定义了一个共享变量什么时候写入,共享变量什么时候对另一个线程可见。
  1. (操作规则)
    • 线程的操作都只能在本地内存进行,不能直接操作主内存
    • 不同线程不能直接 访问其他线程的本地内存,变量需要通过主内存传递
  1. 然后 为了解决可见性,JMM遵循了happens-before规则。然后讲happens-before规则

volatile 关键字解决了什么问题?讲讲volatile

volatile 保证 可见性 和 有序性。

可见性:(两句话)

  1. 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量副本 ( 不仅仅是volatile变量,普通变量也会 ) 值立即刷新回主内存中(volatile写后 有写屏障)

  1. 当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效(所有共享变量的本地副本都无效,不仅仅是volatile变量),重新回到主内存中读取最新共享变量的值在读volatile之前加读屏障

为什么不保证原子性

上面说了,读volatile都是读最新值,但是并没有加锁,当两个线程同时读取volatile并修改时,讲修改的值写回主内存存在写覆盖问题。(主要还是读写时没有加锁

有序性

  1. 禁止指令重排。

happens-before 原则,它解决了可见性,和顺序写

  1. JMM解决可见性使用了synchronized 和 volatile, 它们都遵循 happens-before 规则
  2. 这个规则 约束了编译器 的优化行为,允许优化,但要遵循happens-before规则。
  3. 规则是什么,简单来说是 如果一个操作执行的 结果需要 对另一个 操作可见 ,那么这两个操作之间必须要存在happens-before的关系(不管是不是在一个线程中)
  4. 具体的规则记4-5条:顺序性规则,锁规则,volatile变量规则,传递性规则。