(五) volatile关键字

Java多线程目录

1 背景

理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为提高处理速度,处理器与内存不直接进行交互,而是先将内存中的数据读取到内部缓存。Java多线程安全就是这样产生的。

2 volatile是什么

volatile是Java的一个关键字,在多线程并发中,volatile和synchronized都是重要的关键字,volatile比synchronized更轻量,因为volatile不需要上下文切换,它在多处理器中保证了共享变量的内存可见性

3 内存可见性

变量存储在内存上,在Java中被volatile修饰的变量,Java线程内存模型会确保所有线程看到的这个变量值是一致的,也就是这个变量所在的内存在Java多个线程中看到的是一样的。

3.1 volatile怎样保证内存可见性。

被volatile修饰的变量会确保两件事

  1. 禁止指令重排
    指令重排是个大概念,这里不详细介绍,指令重排就是我们代码的编译优化等,这个是JVM编译时做的,会影响我们的执行顺序,但不会影响执行结果。volatile禁止了指令重排,会让我们的程序更按照我们的想法执行。
  2. 内存可见性
    volatile保证了在我们对volatile修饰的变量进行修改时,线程缓存会立刻刷入主内存。同时为保持多线程看到的变量值一致性,其他线程读取volatil变量会先从主内存读取值,再与线程缓存比较后使用,形成一个线程修改volatile变量,其他线程看到的结果是一致的。

4 volatile的局限性

注意在我们的普通编程中我们并不推荐使用volatile关键字,因为它有各种各样的线程,使用不当达不到多线程并发想要的效果。

4.1 操作并不是原子性

volatile关键字只保证了内存可见性与一致性,但它并不是原子操作,所以我们不能在多个线程内对这个变量都进行写操作。

4.2 运算结果并不依赖当前volatile变量的值,或者只有一个线程在修改volatileb变量。

也就是说我们在普通的多线程并发操作中并不能使用volatile关键字,只有满足上面条件时才使用,来保证volatile变量一修改,其他线程会立马可见。但无法保证多个线程同时对volatile变量写而造成的线程安全问题。

public class ThreadOne implements Runnable {

    volatile int i = 0;

    public void run() {
        i++;
        System.out.println(false + " " + Thread.currentThread().getName() + " " + i);
    }

    public static void main(String[] args) {
        ThreadOne one = new ThreadOne();
        for (int i = 1; i < 200; i++) {
            new Thread(one,"" + i).start();
        }
    }
}

上述代码多个线程对volatile变量i进行了写操作,所以结果一般会出现线程安全问题。

4.3 变量不需要与其他的状态变量共同参与不变约束。

5 总结

  1. Java中每个线程都有单独的内存,所以多线程共享变量会有线程安全问题。
  2. volatile修饰的变量每次改变都会立刻刷入主内存。
  3. 每次使用volatile变量都会先从主内存读取在与线程内存的副本进行比较,后使用。
  4. volatile修饰的变量只能保证内存可见性,它不是原子操作。
  5. volatile会禁止指令重排,代码的顺序就是执行的顺序。

你可能感兴趣的:((五) volatile关键字)