zoukankan      html  css  js  c++  java
  • 多线程之volatile关键字

    轻量级同步机制:volatile关键字

    volatile的作用

    关键作用是使变量在多个线程之间可见

    public class VolatileText {
        public static void main(String[] args) throws InterruptedException {
            Student student=new Student();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    student.GetMethon();
                }
            }).start();
            Thread.sleep(1000);//睡眠之后修改布尔值
            student.GetName(false);//改变布尔值以结束程序运行
        }
        static  class Student
        {
            public boolean flag=true;
            public Student GetName(boolean flag)
            {
                this.flag=flag;
                return  this;
            }
            public  void  GetMethon()
            {
                System.out.println("开始");
                while (flag){//死循环
                }
                System.out.println("结束");
            }
        }
    }
    

    image-20210310223438566

    程序并没有因为我修改之后结束运行,因为线程对共享变量具有不可见性,main线程修改布尔值之后,子线程看不到值的修改。因此要想实现线程的可见性这里可以加上volatile关键字修饰公共变量

    volatile关键字的作用:使线程在强制公共内存中读取变量值,保证可见性

    volatile非原子特性

    public class Text10 extends  Thread {
        private volatile static int count;
        @Override
        public void run() {
           Addcount();
        }
        public   static void  Addcount()
        {
            for (int i = 0; i < 1000; i++) {
                count++;
            }
            System.out.println(Thread.currentThread().getName()+"-->"+count);
        }
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                Text10 text10=new Text10();
                text10.start();
            }
        }
    }
    

    image-20210310232145063

    按道理输出1000的整数倍数才对,但是变量在自增的过程中没有更新到又被读取再修改,因此volatile不具备原子性,正确办法将方法加上synchronized关键字

    image-20210310232349272

    volatile与synchronized比较

    • volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,volatile只能修饰变量,而synchronized可以修饰方法代码块,在开发中使用synchronized比例还是挺大的。

    • 多线程访问volatile变量不会发生阻塞,而synchronized可能会阻塞。

    • volatile能保证数据的可见性,但是不能保证原子性,而synchronized可以保证原子性,也可以保证可见性,因为

    • synchronized会将线程的工作内存和主内存进行同步

    • volatile关键字保证多个线程之间的可见性,synchronized关键字解决线程访问公共资源的同步性。

      区别 synchronized volatile
      使用上 只能用于修饰方法、代码块 只能修饰实例变量或者类关键字
      原子性保证 能保证,锁可以保护数据不被打断 无法保证
      可见性保证 能保证,排它方式使同步代码串行 能保证,可以读取公共变量
      有序性保证 能保证,在同步串行的时候 能保证,禁止JVM以及处理器进行排序
      阻塞情况 会发生阻塞 不会发生阻塞

    常用原子类进行自增自减操作

    i++不是原子操作,除了使用synchronized进行同步,也可以使用AtomicInteger/AtomicLong进行实现

    import java.util.concurrent.atomic.AtomicInteger;
    public class Text10 extends  Thread {
        private   static AtomicInteger count=new AtomicInteger();
        @Override
        public void run() {
           AddCount();
        }
        public   static void  AddCount()
        {
            for (int i = 0; i < 1000; i++) {
                count.getAndIncrement();//相当于i++
                //count.incrementAndGet();//相当于++i
            }
            System.out.println(Thread.currentThread().getName()+"-->"+count.get());
        }
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                Text10 text10=new Text10();
                text10.start();
            }
    
        }
    }
    

    image-20210310234739741

    CAS

    CAS(Compare And Swap)是由硬件实现的,

    CAS可以将read(读)-modify(修改)-write(写)转化为原子操作

    i++自增过程:

    从主内存调取i变量值

    对i值加1

    再把加1过后的值保存到主内存

    CAS原理:在把数据更新到主内存时,再次读取主内存变量的值,如果现在变量的值与期望的值一样就更新。

    image-20210311220552964

    使用CAS原理实现线程安全计数器

    public class CASText {
        public static void main(String[] args) {
            CASControl casControl=new CASControl();
            for (int i = 0; i <10000 ; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+"==>"+casControl.incrementAndGet());
                    }
                }).start();
            }
        }
    }
      class CASControl
    {
        volatile  static  long value;//使用volatile修饰线程可见性
        public  long getValue()
        {
            return  value;
        }
        private  boolean Expect(long oldValue,long newValue)
        {
            synchronized (this)
            {
                if(value==oldValue)
                {
                    value=newValue;
                    return  true;
                }
                else
                    return  false;
            }
        }
        public  long incrementAndGet()
        {
            long oldvalue;
            long newValue;
            do {
                oldvalue=value;
                newValue=oldvalue+1;
    
            }while (!Expect(oldvalue, newValue));
            return  newValue;
        }
    }
    

    image-20210311223403577

    CAS中的ABA问题

    CAS实现原子操作背后有一个假设:共享变量当前值与当前线程提供的期望值相同,就认为变量没有被其他线程过。

    实际上这种假设不一定成立,假如count=0

    A线程对count值修改为10

    B线程对count值修改为30

    C线程对count值修改为0

    当前线程看到count=0,不能认为count没有被其他线程更新,这种结果是否能被接受?

    这就是CAS中的ABS问题即共享变量经过了A=》B=》A的更改

    是否能够接受ABA问题跟算法实现有关

    如果想要规避ABA问题,可以为共享变量引入一个修订号,或者时间戳,每次修改共享变量值,相应的修订号加1.,就会变更为[A,0]=>[B,1]=>[A,2],每次对共享变量的修改都会导致共享变量的增加,通过这个标识就可以判断。AtomicStampedReference类就是基于这个思想产生的。

    原子变量类

    原子类变量是基于CAS实现的,当对共享变量进行read(读)-modify(修改)-write(写)操作时,通过原子类变量可以保障原子性与可见性,对变量的read(读)-modify(修改)-write(写)操作不是指一个简单的赋值,而是变量的新值,依赖变量的旧值,如自增操作i++,由于volatile只能保证原子的可见性,而不能保证原子性,原变量类内部就是一个借助volatile变量,并且保障了该变量的read-modify-wirte操作的原子性,有时把原子变量看作一个增强的volatile变量,原子变量类有12个

    分组 原子变量类
    基础数据型 AtomicInteger、AtomicLong、AtomicBoolean
    数组型 AtomicIntegerArry、AtomicLongArry、AtomicReferenceArry
    字段更新器 AtomocIntegerFiledUpdater、AtomicLongFieldUpdate、AtomicReferenceFiledUpdater
    引用型 AtomicReference、AtomicStampedReference、AtomicMarkableReference

    使用AtomicLong定义计数器

    import java.util.Random;
    import java.util.concurrent.atomic.AtomicLong;
    
    public class Text15 {
        //构造方法私有化
        private  Text15(){}
        //私有静态对象
        private  static final Text15 text=new Text15();
        //公共静态方法返回该类的实例
        public  static  Text15 getInstance()
        {
            return  text;
        }
        //使用原子类记录保存请求总数 成功数 失败数
        private  final AtomicLong RequestCount=new AtomicLong(0);
        private  final AtomicLong SuccessCount=new AtomicLong(0);
        private  final AtomicLong FailCount=new AtomicLong(0);
        //进行自增
        public  void  RequestCount()
        {
            RequestCount.incrementAndGet();
        }
        public  void  SuccessCount()
        {
            SuccessCount.incrementAndGet();
        }
        public  void  FailCount()
        {
            FailCount.incrementAndGet();
        }
        //查看总数
        public  long GetRequestCount()
        {
            return  RequestCount.get();
        }
        public  long  GetSuccessCount()
        {
            return SuccessCount.get();
        }
        public  long  GetFailCount()
        {
            return FailCount.get();
        }
    }
    class  Text16
    {
        public static void main(String[] args) throws InterruptedException {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i <1000 ; i++) {
                        Text15.getInstance().RequestCount();//请求数量
                        int num=new Random().nextInt();
                        if(num%2==0)//如果是偶数就成功
                        {
                            Text15.getInstance().SuccessCount();
                        }
                        else
                            Text15.getInstance().FailCount();
                    }
                }
            }).start();
            Thread.sleep(1000);
            System.out.println("请求总数:"+Text15.getInstance().GetRequestCount());
            System.out.println("请求成功"+Text15.getInstance().GetSuccessCount());
            System.out.println("请求失败"+Text15.getInstance().GetFailCount());
        }
    }
    

    image-20210315213254758

  • 相关阅读:
    Spring(九)之事件处理
    Spring(八)之基于Java配置
    Spring(七)之基于注解配置
    Spring(六)之自动装配
    Spring(五)之Bean定义继承和依赖注入
    Spring(四)之Bean生命周期、BeanPost处理
    开源 视频会议 收藏
    摄像头拍照录相代码,没摄像头测试,
    什么是ICE (Internet Communications Engine)
    AForge.NET Framework-2.2.5
  • 原文地址:https://www.cnblogs.com/cg-ww/p/14540450.html
Copyright © 2011-2022 走看看