0%

jmm

  • 原子性
    原子性是指一个操作是不可中断的. 即使在多线程环境下, 一个操作一旦开始, 不会被其他线程干扰.
  • 可见性
    可见性是指当一个线程修改某个共享变量时, 其他线程是否能立即知道这个修改.
  • 有序性
    在并发时, 程序的执行可能出现乱序. 给人的直观感觉就是: 写在前面的代码, 会在后面执行.
    原因是程序执行时, 可能会进行指令重排, 重排后的指令与原指令顺序未必一致.

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

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

1. volatile

volatile 保证被申明的变量的可见性, 读写的有序性, 但不能保证变量操作的原子性.

每次读取变量, 都要从共享内存中复制到工作内存中; 同样, 每次修改变量, 都要从工作内存复制到共享内存中.

Happen-Before 规则保证 volatile 修饰的变量, 写必定发生在读之前.

2. volatile vs static

volatile 修饰的变量在多线程情况下的可见性和有序性, 但不能保证原子性.

static 修饰的变量在多个实例中只有一份, 即多实例共享变量.

volatile 和 static 能同时修饰变量, 他们没有冲突.

3. volatile vs final

volatile 和 final 不能能同时修饰变量.

final 修饰的变量不可改变, 所以能保证线程安全, 但 volatile 不能.