Java volatile关键字详解

volatile是什么

volatile关键字是Java虚拟机提供的轻量级同步机制

JMM是什么

JMM是一个抽象概念,定义了共享内存系统中多线程程序读写操作共享内存的行为规范,保证共享内存的正确性(可见性、有序性、原子性)。JMM中关于同步的规定:

  • 线程解锁前必须把共享变量最新的值刷新回主存中
  • 线程加锁前必须从主存中读取共享变量最新的值到自己的工作内存
  • 加锁和解锁必须是同一把

volatile关键字作用

  • 保证内存可见性
  • 禁止指令重排,保证有序性

volatile内存可见性代码演示

不加volatile

class MyData{
    int i = 0;

    public void addTo100() {
        this.i = 100;
    }
}

public class Demo {
    public static void main(String[] args) {
        // 创建共享变量
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "启动!");
            // 睡眠3s
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo100();
            System.out.println(Thread.currentThread().getName() + "数据修改完成!");
        }, "线程A").start();
        while (myData.i == 0) { // 主线程阻塞等待

        }
        System.out.println(Thread.currentThread().getName() + "检测到变量被修改,程序退出!");
    }
}

运行结果
在这里插入图片描述
主线程进入阻塞等待,线程A对变量的修改对主线程是不可见的

加volatile

class MyData{
    volatile int i = 0;

    public void addTo100() {
        this.i = 100;
    }
}

public class Demo {
    public static void main(String[] args) {
        // 创建共享变量
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "启动!");
            // 睡眠3s
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo100();
            System.out.println(Thread.currentThread().getName() + "数据修改完成!");
        }, "线程A").start();
        while (myData.i == 0) { // 主线程阻塞等待

        }
        System.out.println(Thread.currentThread().getName() + "检测到变量被修改,程序退出!");
    }
}

运行结果
在这里插入图片描述
线程A修改变量完成后立马被主线程感知到,程序退出

volatile不保证原子性代码演示

class MyData{
    volatile int i = 0;

    public void add() {
        this.i ++;
    }
}

public class Demo {
    public static void main(String[] args) {
        // 创建共享变量
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.add();
                }
            }, String.valueOf(i)).start();
        }


        while (Thread.activeCount() > 2) { // 主线程礼让
            Thread.yield();
        }
        System.out.println("最终结果:" + myData.i);
    }
}

运行结果在这里插入图片描述
假设volatile可以保证原子性,正确的结果应该是20000。i++可以看作是i = i+1,此命令包含三步操作:1.取出i值 2.执行i+1 3.重新赋值给i。如果线程A修改了i,但还没来得及写回主存,此时cpu被线程B抢占后也进行了修改,此时线程B拿到的就不是i的最新值,因此计算结果有偏差

volatile保证原子性解决

直接上代码

class MyData{
    volatile AtomicInteger i = new AtomicInteger(); // 默认为0

    public void add() {
        this.i.addAndGet(1);
    }
}

public class Demo {
    public static void main(String[] args) {
        // 创建共享变量
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.add();
                }
            }, String.valueOf(i)).start();
        }


        while (Thread.activeCount() > 2) { // 主线程礼让
            Thread.yield();
        }
        System.out.println("最终结果:" + myData.i);
    }
}

运行结果
在这里插入图片描述
JUC(java.util.concurrent)包中的AtomicXXX类通过CAS解决了原子性问题。
CAS:Compare And Swap(比较并交换),核心思想就是在变量重新赋值前,把线程工作空间内的变量副本值和变量实际内存空间中变量值做比较,如果相等就赋值,不相等就进入循环判断。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值