volatile关键字复习

volatile关键字是Java中用于多线程的轻量级同步机制,它确保变量的可见性但不保证原子性。在多线程环境下,volatile能保证一个线程修改变量值后其他线程能立即看到变化,避免死锁问题。然而,对于复合操作,如自增,volatile无法保证原子性,可能导致线程不安全。此外,volatile还禁止指令重排序,防止因重排序导致的数据不一致。在需要原子性操作时,可以使用synchronized或AtomicInteger类来保证。
摘要由CSDN通过智能技术生成

volatile关键字是Java虚拟机提供的轻量级的同步机制
它具有以下三个特性:
1)保证可见性
2)无法确保原子性
3)禁止指令重排序
线程操作变量的工作流程

在这里插入图片描述
1.普通情况下,在多线程环境下,一个线程在完成对变量拷贝副本的修改等操作后,其他线程是无法立即得知的,可能会导致死循环问题。但当该变量被volatile关键字修饰后,某一线程修改了这个变量的值,其他变量会立即得知该变量的值已改变,这就是volatile能保证可见性的体现。
验证代码:

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

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{
                TimeUnit.SECONDS.sleep(3);
            }catch (InterruptedException e){
                e.printStackTrace();
            }

            mydata.AddTo60();

            System.out.println(Thread.currentThread().getName()+"\t updated number value->"+mydata.number);
         },"AAA").start();


        //第2个线程就是我们的main线程
        //number未加volatile修饰时,此时main线程并不知道主内存中的number值已经等于60,就会一直在这里死循环等待
         while(mydata.number == 0){
             //main线程就一直在这里等待循环,直到number不等于0
         }

         System.out.println(Thread.currentThread().getName() +"\t int type,此时number的值为:"+mydata.number);
 }
}

未加volatile关键字修饰时,运行结果:
在这里插入图片描述上述代码运行,运行结果:
在这里插入图片描述
2.原子性指的是不可分割,完整性,也即某个线程正在做某个具体的业务时,中间不可以被加塞或者分割,需要整体完整, 要么同时成功,要么同时失败。
使用volatile关键字修饰的变量在多线程高并发的情况下,同样是线程不安全。因为在java中运算并不是原子性操作。原因如图,
在这里插入图片描述验证一下,贴代码:

class MyData{
    volatile int number = 0;
    public void AddPlusPlus()
    {
        //此时number已经被volatile关键字修饰
        number++;
    }
}

public class VolatileDemo {
public static void main(String[] args)
    {
        MyData data = new MyData();

        for (int i = 0; i <20 ; i++) {

            new Thread(()->{
                for (int j = 1; j <=1000 ; j++) {
                    data.AddPlusPlus();
                    data.myaddAtomic();
                }
            },String.valueOf(i)).start();

        }

        //需要等待上面20个线程全部计算完成后,再利用main线程取得最终的结果值
        while(Thread.activeCount() >2){
            Thread.yield();
        }

        //结果并不等于20000,证明volatile关键字不保证原子性
        //可能导致写丢失的情况
        System.out.println(Thread.currentThread().getName()+"\t Int Type,final number:"+data.number);
    }
}

运行结果:在这里插入图片描述值不等于20000,且每次运行结果都不一样。

使用AtomicInteger类,

class MyData{
    AtomicInteger atomicInteger = new AtomicInteger();

    public void myaddAtomic()
    {
        atomicInteger.getAndIncrement();
    }
}

public class VolatileDemo {
   public static void main(String[] args)
    {
        MyData data = new MyData();

        for (int i = 0; i <20 ; i++) {

            new Thread(()->{
                for (int j = 1; j <=1000 ; j++) {
                    data.AddPlusPlus();
                    data.myaddAtomic();
                }
            },String.valueOf(i)).start();

        }

        //需要等待上面20个线程全部计算完成后,再利用main线程取得最终的结果值
        while(Thread.activeCount() >2){
            Thread.yield();
        }

       
       System.out.println(Thread.currentThread().getName() +"\t AtomicInteger Type,final number,此时number的值为:"
                +data.atomicInteger);

    }
}

运行结果,在这里插入图片描述
实际上,++操作在底层编译阶段是进行了三个步骤!
所以,在多线程的情况下,可能会出现丢失写回主内存的值的情况。
为了避免这种情况出现,我们可以使用sychronized关键字修饰方法或者使用AtomicInteger类(推荐使用的)。

3.禁止重排序。什么是重排序?
重排序是指令的重排序。为了提高性能,编译器和处理器常常会对指令做重排序,重排序就会导致多线程执行的时候有数据不一致问题,导致程序结果不是理想结果。

使用volatile关键字修饰后,就不会发生重排序。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值