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

  • 相关阅读:
    Creating a generic Web Parts for hosting ASP.NET User Controls
    Speed Up SQL Server Apps 提高SQL Server应用程序的运行效率 (Part 1)
    How to use CreateChildContorls method inherited from System.Web.UI.Control
    How to quickly access Web Part Management Page
    SQL Script tips for MS SQL Server
    How to enable single signon service on the SPS
    A brief summary of UML & Rational Rose – Use Case Diagram, Part II
    Borland Together for Visual Studio.Net V2.0 安装问题
    Speed Up SQL Server Apps 提高SQL Server应用程序的运行效率 (Part 2)
    体验ReSharper V1.0 for VS.Net 2003 Part I
  • 原文地址:https://www.cnblogs.com/cg-ww/p/14540450.html
Copyright © 2011-2022 走看看