volatile关键字

110 阅读2分钟

volatile关键字的特性

1.不能保证原子性:
2.能避免指令重排:
	在编译和执行代码时,出于优化考虑,会重排指令;大多数场景下指令重排不会影响结果,但在多线程环境下可能会有问题
3.使变量在线程间都可见:
	在线程内存中,如果进行写操作,能够立即写回到主内存中,如此该值会在其他线程内存中失效;也就是说,其他内存如果想要再次读取该值,就需要到主内存中去读

特性解释

原子性: 一个操作或多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行
指令重排:
	概念: 指JVM在编译Java代码或CPU在执行JVM字节码时,对现有的指令顺序进行重新排序
	目的: 在不改变(单线程)程序执行结果的前提下,优化程序的运行效率

不能保证原子性

代码展示

public class VOLATILETest {

    /* int的默认值为0 */
    private static volatile int i;

    public static void add() {

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        i++;

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        for (int j = 0; j < 1000; j++) new Thread(VOLATILETest::add).start();

        System.out.println("i = " + i);

    }
}

打印展示

不能保证原子性

使变量在线程间都可见

变量在线程间不可见

代码展示

public class VolatileTest extends Thread {

    /* int的默认值为0 */
    private int i;

    private boolean stopFlag = true;

    public void run() {

        while (stopFlag) {
            i++;
        }
    }

    public static void main(String[] args) {

        VolatileTest volatileTest = new VolatileTest();

        volatileTest.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        volatileTest.stopFlag = false;

        System.out.println(volatileTest.i);

    }
}

打印展示

程序无法正常结束

不可见变量

问题原因

在主线程中修改变量是无法作用到其他线程中的

变量在线程间可见

代码展示

public class VolatileTest extends Thread {

    /* int的默认值为0 */
    private int i;

    private volatile boolean stopFlag = true;

    public void run() {

        while (stopFlag) {
            i++;
        }
    }

    public static void main(String[] args) {

        VolatileTest volatileTest = new VolatileTest();

        volatileTest.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        volatileTest.stopFlag = false;

        System.out.println(volatileTest.i);

    }
}

打印展示

程序正常退出

变量在线程间可见

ConcurrentHashMap

HashMap是线程不安全的,当多个线程同时操作HashMap,可能会出现因抢占线程而出现数据问题;而ConcurrentHashMap是一个能实现并发的HashMap

baseCount变量

用来存储ConcurrentHashMap的长度

baseCount变量使用了volatile关键字修饰,作用为:
	若多个线程同时读写ConcurrentHashMap对象,对长度的修改能立即让其他线程感知,由此保证多线程同步

baseCount