Skip to main content

线程安全

David LiuAbout 2 min

线程安全

并发编程三要素

  • 可见性,一个线程对共享变量修改,另外的线程能立即看到最新值。
  • 有序性,一个线程内代码按编写顺序执行(多线程下不能)由于指令重排序问题,代码的执行顺序未必就是编写代码时候的顺序。
  • 原子性,一个线程内多行代码以一个整体运行,期间不难有其他线程的代码插队

原子性

  • 起因:多线程下,不同线程的指令发生了交错导致的共享变量的读写混乱
  • 解决:用悲观锁或乐观锁解决,volatile 并不能解决原子性

可见性

  • 起因:由于编译器优化、或缓存优化、或 CPU 指令重排序优化导致的对共享变量所做的修改另外的线程看不到
  • 解决:用 volatile 修饰共享变量,能够防止编译器等优化发生,让一个线程对共享变量的修改对另一个线程可见

有序性

  • 起因:由于编译器优化、或缓存优化、或 CPU 指令重排序优化导致指令的实际执行顺序与编写顺序不一致
  • 解决:用 volatile 修饰共享变量会在读、写共享变量时加入不同的屏障,阻止其他读写操作越过屏障,从而达到阻止重排序的效果
  • 注意:
    • volatile 变量写加的屏障是阻止上方其它写操作越过屏障排到 volatile 变量写之下
    • volatile 变量读加的屏障是阻止下方其它读操作越过屏障排到 volatile 变量读之上
    • volatile 读写加入的屏障只能防止同一线程内的指令重排

volatile 能够保证共享变量的可见性与有序性,但是并不难保证原子性

volatile 关键字可以禁止指令进行重排序优化

原子性的实现

  • CAS

指令交错,就会导致问题

在 Java 中,Unsafe 类提供了三个开箱即用的内存屏障相关的方法,屏蔽了操作系统底层的差异: