Volatile-2.不保证原子性

(1)代码理解

变量使用了volatile关键字

package automitic;

public class Mydata {
    volatile  int num=0;
    public  void addto60(){
         this.num=60;
    }
    public void  addplusplus(){
        num++;
    }
}

开启20个前程执行1000次num++

package automitic;

//验证volatile不保证原子性

public class VolatileDemo {
    public static void main(String[] args) {
        Mydata mydata=new Mydata();  //资源类
        //创建20个线程,每个线程操作num++ 1000次
        for(int i=0; i<20;i++){
            new Thread(()->{
                for(int j=0;j<1000;j++)
                mydata.addplusplus();
            },String.valueOf(i)).start();
        }
        //等待20个线程执行结束后,用main线程取值,如果volatile保证原子性,那应该会得到值20000
        while (Thread.activeCount()>2){  
            //这里为什么用2,因为默认后台就会有两个线程,main线程和GC线程
            Thread.yield();
        }
        //控制多线程结束后再..的最好方法
       System.out.println(Thread.currentThread().getName()+"\t任务完成,num值现在为:"+mydata.num);
   
    }

}

执行结果

在这里插入图片描述
在这里插入图片描述

发现执行多次的值都不一样,也都不是想当然的20000,当然也有是20000的可能,偶然而已。

所以证明volatile不保证原子性。

(2)原理

volatile不保证原子性,也再次体现了它是一个轻量级,乞丐版

  • 为什么num++的值小于20000

    因为出现了多线程,出现了丢失写值得情况

在这里插入图片描述

比如线程1,2,3都从主物理内存中拿到了num初始值0,在各自得工作内存中,各自自增1, 三个线程都要向主物理内存中写,1线程写入主内存,2,3线程挂起,然后num得值变为1,通知2,3,进程num值已经为1了。但是因为线程非常快,线程2或者3又可能再次把1又写入了主内存,写覆盖,造成了写数据丢失。所以最后得值就很大可能低于20000.恰好等于20000的几率极小。

number++在多线程下是非线程安全的,加synchronized,就能保证是线程安全了

package automitic;

public class Mydata {
    volatile  int num=0;
    public  void addto60(){
         this.num=60;
    }
    public synchronized  void  addplusplus(){
        num++;
    }
}

加了之后就计算都是20000了

在这里插入图片描述

但是使用synchronized,有点大材小用

(3)不使用synchronized,volatile如何解决原子性

//使用JUP下的AtomicInteger
AtomicInteger atomicInteger= new AtomicInteger();//初始值为0

package Atomic;

import java.util.concurrent.atomic.AtomicInteger;

public class Mydata {
    //基本类型
    volatile  int num=0;
    public  void addto60(){
         this.num=60;
    }
    public   void  addplusplus(){
        num++;
    }
    //使用带原子性的num++
    AtomicInteger  atomicInteger= new AtomicInteger();//初始值为0
    public void addAtomic(){
        atomicInteger.getAndIncrement();  //增1
    }
}

package Atomic;

//验证volatile不保证原子性

public class VolatileDemo {
    public static void main(String[] args) {
        Mydata mydata=new Mydata();  //资源类
        //创建20个线程,每个线程操作num++ 1000次
        for(int i=0; i<20;i++){
            new Thread(()->{
                for(int j=0;j<1000;j++)
           //     mydata.addplusplus();
                mydata.addmyAtomic();
            },String.valueOf(i)).start();
        }
        //等待20个线程执行结束后,用main线程取值,如果volatile保证原子性,那应该会得到值20000

        while (Thread.activeCount()>2){  //这里为什么用2,因为默认后台就会有两个线程,main线程和GC线程
            Thread.yield();
        }
        //控制多线程结束后再..的最好方法
       // System.out.println(Thread.currentThread().getName()+"\t任务完成,int类型 num值现在为:"+mydata.num);
        System.out.println(Thread.currentThread().getName()+"\t任务完成,AtomicInterger类型 值在为:"+mydata.atomicInteger);


    }

}

运行结果,就一直是20000

在这里插入图片描述

本节新学知识点

1.java后台默认的两个线程 :main, GC
2.主线程main等待其他线程结束后再执行的最佳方法


        while (Thread.activeCount()>2){  //这里为什么用2,因为默认后台就会有两个线程,main线程和GC线程
            Thread.yield();      //控制多线程结束后再..的最好方法

        }
  1. Thread.yield();
    使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。

PS:学习笔记,尚硅谷,周阳大佬

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值