Java中volatile的用法

本文探讨了Java中volatile关键字的作用,它用于辅助保证线程安全,确保内存可见性并防止指令重排序,但不保证原子性。通过示例代码,解释了在多线程环境下,volatile如何避免内存可见性问题导致的线程安全问题,同时对比了synchronized的使用,指出volatile在某些场景下的高效性。

volatile

起到的效果是辅助保证线程安全,volatile能够禁止指令重排序,保证内存可见性,但是不保证原子性。主要用于读写同一个变量。

观察如下代码:

public class TestVolatile {
    static class Counter{
        public  int flag = 0;
        public int x = 0;
    }
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                    while(counter.flag == 0){
                        // do nothing
                    }
                counter.x = 1;
                System.out.println("线程1 循环结束!!");
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while(counter.x == 0){
                    Scanner input = new Scanner(System.in);
                    System.out.println("输入一个整数:");
                    counter.flag = input.nextInt();
                }
            }
        };
        t2.start();
    }
}

创建2个线程,线程1判断如果flag = 0,那么一直执行循环,线程2输入一个整数,赋值给flag,当输入的数不是0时,线程1 中的循环应该结束~~

但是执行代码发现,循环并没有结束!!
在这里插入图片描述
这就是由于内存可见性导致的。

上述代码中,线程1 的while 循环里面啥都没做,所以这个循环执行的速度非常快,如果不做任何优化,此时CPU就需要频繁的读取内存数据,但是读取内存中的数据会比读取寄存器中的数据慢很多,所以编译器就会把这个读操作优化为只从内存(主内存)里读取一次,后续都直接读寄存器/缓存(工作内存)。

但是线程2 修改的时候,读到用户输入的值,在把读到的值在写回内存中去,但是线程1并没有重新读取内存中的值,还是从寄存器中读取(寄存器里的值未发生改变还是 0 ),并不知道主内存的值已经改变了。所以这里也就出现了线程安全问题。

在这个场景中,使用synchronized也能解决问题,但是synchronized还是有点重,用synchronized之后,效率就会大大折扣,所以我们采用更轻量级的volatile,也相对高效一些(不涉及锁竞争,也不涉及线程调度)。

给flag 加上volatile

public class TestVolatile {
    static class Counter{
        volatile public  int flag = 0;
        public int x = 0;
    }
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                    while(counter.flag == 0){
                        // do nothing
                    }
                counter.x = 1;
                System.out.println("线程1 循环结束!!");
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while(counter.x == 0){
                    Scanner input = new Scanner(System.in);
                    System.out.println("输入一个整数:");
                    counter.flag = input.nextInt();
                }
            }
        };
        t2.start();
    }
}

在这里插入图片描述

在这种一个线程写,一个线程读的场景下,使用volatile就可以一定程度的保证线程安全,volatile保证了内存可见性,禁止指令重排序,但是不保证原子性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值