Java 中的 volatile

volatile 是Java虚拟机提供的轻量级的同步机制。

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

可见性

当多个线程操作同一个变量时,每个线程都会将该变量拷贝一份到自己工作内存中,然后线程完成操作之后,将变量写回主内存。因此可见性就是一个线程在修改变量之后,回通知其他线程,告知改变量已经被修改了。
首先创建一个资源类

class MyData {
    volatile int number = 0;
    public void setNumber () {
        this.number = 60;
    }
    public void numberPlus() {
        this.number++;
    }
}
// 多线程下测试
public static void main(String[] args) {
   MyData myData = new MyData();
   new Thread(() -> {
            System.out.println(myData.number);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.setNumber();
            System.out.println(myData.number);
        }, "AAA").start();;

        while(myData.number == 0) {
        }
}

当使用 volatile 修饰 number 之后,程序回输出 0 和 60 之后执行完成退出。
当没有使用 volatile 修饰 number,程序回输出 0 和 60 之后一直保持执行(死循环),不退出。
由此可以看出,volatile 修饰的变量在修改后,回通知其他线程,保证可见性。

原子性

class MyData {
    volatile int number = 0;
    public void setNumber () {
        this.number = 60;
    }
    public void numberPlus() {
        this.number++;
    }
    AtomicInteger atomicInteger = new AtomicInteger(); // 默认为0
    public void atomicAdd() {
        atomicInteger.getAndIncrement();
    }
}
public static void main(String[] args) {
    MyData myData = new MyData();
    for(int i=0; i<20; i++) {
            new Thread(() -> {
                for(int j=0; j<1000; j++) {
                    myData.numberPlus();
                    myData.atomicAdd();
                }
            }, String.valueOf(i)).start();;
        }

        while(Thread.activeCount() >2) {
            Thread.yield();
        }
        System.out.println(myData.number);
        System.out.println(myData.atomicInteger);
}

程序输出 number 的值小于20000,atomicIntege r等于20000。
因为 volatile 不保证原子性,所以在多线程下操作 number 会出现值被覆盖的问题,也就是一个线程还没有完成++,另一个线程就取出了 number 进行++, 最后导致值小于了 20000。
解决 volatile 原子性问题,可以使用 AtomicInteger

指令重排

为了提高性能,编译器和处理器常常会进行指令重排,在多线程环境下,由于指令重排,导致代码的执行顺序和我们书写代码顺序不一致,最终变量能否保持一致是无法确定的,结果无法预测。

posted @ 2019-06-19 16:50  Godfunc  阅读(215)  评论(0编辑  收藏  举报