多线程——Volatile 关键字
一、概念
在前一篇文章中,我们详细介绍了 Java 内存模型,我们可以发现 Java 内存模型都是围绕着在并发过程中如何处理原子性、可见性和有序性三个特征来建立的,简单回顾一下为什么会产生这三个特性:
- 原子性:比如一段代码运行在服务器,有一段代码会被所有线程访问,每一个线程都会修改里面的共享变量值,为了保证一个线程在修改的时候,其他的线程不会进来捣乱,就引入了原子性的概念,保证原子性的手段最常用的就是锁机制
- 可见性:我们知道内存分为工作内存和主内存,每个线程都有其独占的工作内存,所有线程共享主内存;可见性就是当一个线程修改了共享内存的值时,其他线程都能够立即得知这个修改
- 有序性:硬件层面由于为了压榨速度超快的 CPU,引入了 CPU 的乱序执行操作优化,映射到 Java 中就是指令重排序;然后有些地方指令重排序会产生问题,所以需要限制指令重排序,这就是有序性
其中原子性主要由锁机制来保证,锁机制同样也能保证可见性和有序性,这些我们后面会详细讲。本篇文章中我们主要来讲保证可见性和有序性的一种轻量级的手段—— Volatile 关键字
关键字 Volatile 可以说是 JVM 提供的最轻量级的同步机制,但是开发中基本不使用 Volatile,一来并发情况下最需要保证原子性, Volatile 并不能保证原子性;二来 Synchronized 比较全面而且效率也并不会太低于 Volatile,所以普遍都是使用 Synchronized 作为同步手段
不过理解了 Volatile,就会对内存模型和多线程操作的其他特性就会有更深的理解
二、Volatile保证可见性
当一个变量被 volatile 修饰之后,就能够保证此变量对所有的线程的可见性——当一个线程修改了这个变量的值,新值对于其他线程都是立即得知的
普通变量并不能保证这一点,普通变量的值在线程间传递时据需要通过主内存来完成;比如线程 A 修改了一个普通变量的值,然后向主内存进行回写,另外一条线程 B 只有在线程 A 完成了回写之后再对主内存进行读取操作,新变量值才会对线程 B 可见
Volatile 变量对于所有线程都是立即可见的,对 Volatile 变量的所有写操作都能立即反映到其他线程之中,但是基于 volatile 变量的运算在并发情况下并不是线程安全的(不能保证原子性);这句话是什么意思呢?来分析下面的示例代码:
public class VolatileTest {
public static volatile int race = 0;
public static void increase(){
race++