volatile详解

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

volatile三大特性

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排(有序性)

可见性:

每个线程创建时JVM都会为他创建一个工作内存,工作内存是每个线程的私有数据区域,Java内存模型中所有的变量都存在主内存,线程对变量的读取和赋值都要在工作内存中完成。所以首先要将变量拷贝到工作内存,修改后再写回主内存,如果有多个线程都读取了这个变量。修改后则需要通知那些线程。这个通知的行为便成为可见性。详见下图。
volatile详解_第1张图片

volatile可见性验证

/**
 * 1.验证volatile的可见性
 *  1.1 假如int number = 0; number变量之前没有加volatile关键字修饰
 *  1.2 添加了volatile,可以解决可见性问题
 **/
public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t come in");

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);

        }, "AAA").start();

        while (myData.number == 0) {
            //main线程一直在等待循环,直到number不等于0
        }

        System.out.println(Thread.currentThread().getName()+"\t mission is over");
    }
}

class MyData{
    int number = 0;
    public void addTo60(){
        this.number = 60;
    }
}

运行结束,AAA线程已经将工作内存中的number改为60,并且更新到了主内存,main线程却获取不到。
在这里插入图片描述
当number采用volatile修饰时,main便能访问。证明了volatile的可见性

volatile int number = 0;

volatile详解_第2张图片

不保证原子性:

volatile不保证原子性验证

public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++) {
                    myData.addPlusPlus();
                }
            }, String.valueOf(i)).start();
        }

        //需要等待上面20个线程都是计算完成,然后main线程取得结果
        while (Thread.activeCount() > 2) {//2是因为有main线程和GC线程
            Thread.yield();//main线程把自己的cpu执行的时间让掉
        }
        System.out.println(Thread.currentThread().getName() + "\t finally number value:" +myData.number);
    }
}

class MyData{
    volatile int number = 0;
    public void addPlusPlus(){
        number++;
    }
}

正常应该是20000.多次验证这里都是小于20000.证明了每个线程去访问这个变量并不是原子的
可以采用AtomicInteger来保证原子性

在这里插入图片描述

禁止指令重排

单线程没有指令重排,多线程才会有
volatile详解_第3张图片
指令重排不能说好与不好,根据实际的业务场景来决定是否指令重排。指令重排会导致数据不一致

你可能感兴趣的:(java)