zoukankan      html  css  js  c++  java
  • 【JUC】如何安全实现count++以及CAS为什么要出现?悲观、乐观锁又是啥?

    我们实现一个例子。

    我们有一个count变量。建立10个线程,每个线程都对count加1000次。(count++)

    public class cas {
        static int count=0;
        public static void main(String[] args) throws InterruptedException {
            Thread[] threads=new Thread[10];
            for (int i = 0; i < 10; i++) {
    
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 1000; i++) {
                            count++;
                        }
                        return;
                    }
                });
                thread.start();
                threads[i]=thread;
            }
    //        for(int i=0;i<10;i++)
    //        {
    //            threads[i].join();
    //        }
    
            Thread.sleep(1000);//等所有线程完成计算
            System.out.println(count);
        }
    }

     显而易见 答案并非我们所愿是10000.

    这是因为count++这个操作在jvm引擎里执行步骤是:

    (1)A=count

    (2)B=A+1

    (3)count=B

    这样有可能有几个线程当拿到count时 还没进行加法 别的线程就已经加完了

    (举个例子:

    线程小明拿到count 线程小刚拿到count。此时count都为200

    CPU对于小明照顾一点,让他先行完成了count++的运算 此时count变为201.

    小刚拿来的count副本好好放在自己工作内存 不知道已经变了

    所以还是老老实实做到count++将count变为201.此时小刚浪费了自己的一次++的机会。因为白加了)

     加了volatile更加离谱。想知道为什么 欢迎看我的博客:【JMM】java内存模型及volatile关键字底层 

    还有一种方法就是原子类 AtomicInteger不过这次我们就搞了(想不搞的。。。)

    public class cas {
    //    static volatile int count=0;
        static AtomicInteger count=new AtomicInteger();
        public static void main(String[] args) throws InterruptedException {
            count.set(0);
            Thread[] threads=new Thread[10];
            for (int i = 0; i < 10; i++) {
    
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 1000; i++) {
                            count.getAndIncrement();
                        }
                        return;
                    }
                });
                thread.start();
                threads[i]=thread;
            }
    //        for(int i=0;i<10;i++)
    //        {
    //            threads[i].join();
    //        }
    
            Thread.sleep(1000);//等所有线程完成计算
            System.out.println(count);
        }
    }

     

     那这必须没问题啊。

    赶紧肝CAS吧。。。

    我们回到那个三部曲

    (1)A=count

    (2)B=A+1

    (3)count=B

    如果对于整个方法加synchronized或者reentrantLockk非常慢啊 我们的线程小子们都变成了串行的了。这还能叫多线程?

    我们对于第三步(3)进行修改以及升级

    (1)A=count

    (2)B=A+1

    (3)1.获得锁

      2.获取count最新值new 

      3.CAS方法进行判断:是否当前count==A。相等则把B=count。不相等则循环等待相等的机会到来。

      4.释放锁

    public class cas {
        static volatile int count=0;
        public static int getCount(){return count;}
        public static synchronized boolean compareAndSwap(int exceptCount,int now){
            if(getCount()==exceptCount)
            {
                count=now;
                return true;
            }
            else
                return false;
        }
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
    
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 1000; i++) {
                            while(!compareAndSwap(count,count+1)){}//自旋
                        }
                        return;
                    }
                });
                thread.start();
            }
    
            Thread.sleep(1000);//等所有线程完成计算
            System.out.println(count);
        }
    }

    自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

    CAS的问题(ABA问题)

     

    ABA问题的修改  对于比较的数据 变为一个pair 数据+版本号 

     

     

    5.悲观锁、乐观锁

    悲观锁指的就是我们平常使用的加锁机制,它假设我们总是处于最坏的情况下,如果不加锁数据完整性就会被破坏。

    而乐观锁指是一种基于冲突检测的方法,检测到冲突时操作就会失败。

    这个情绪是描述操作系统对于资源的。

    比如互斥锁 就是悲观锁,操作系统对于这个资源是悲观的,认为只要多个线程请求它,肯定会出错。

    比如CAS就是乐观锁 他其实和锁并没有关系 

    6.原子类 atomicInteger的自增方法

     

  • 相关阅读:
    程序员第一定律:关于技能与收入
    Android——全屏显示的两种方式
    Android与JavaScript方法相互调用
    IT职场人生:找谁占卜
    Linux 2.6.23开始使用CFS(complete fair schedule),线程Priority不再有效
    如何查看一份linux kernel source的版本?
    tar解包的时候如何exclude一些目录
    rsync通过SSH来同步两台机器上的内容
    ArchLinux下配置TPLink WN550G PCI网卡为无线AP
    配置Linux下的时间服务器,让一批机器和一台机器时间同步
  • 原文地址:https://www.cnblogs.com/cckong/p/14444374.html
Copyright © 2011-2022 走看看