zoukankan      html  css  js  c++  java
  • CAS(Compare And Swap) 算法

    CAS 也叫自旋锁,用来保证操作的一致性,比如用2个线程同时对一个变量累加1000,000次,得到的结果可能比2000,000少:

    package juc.test.cas;
    
    import org.junit.Test;
    
    import java.util.concurrent.CountDownLatch;
    
    public class NonAtomicTest {
    
        static long var = 0 ;
        final int totalThread = 2;
        final long timesEveryThread = 1000000;
        @Test
        public void testAtomicLong(){
    
            CountDownLatch cdl = new CountDownLatch(totalThread);  // thread count
    
            for (int j = 0; j < totalThread; j++) {
                new Thread(){
                    @Override
                    public void run() {
                        for (int i = 0; i < timesEveryThread; i++) {
                            var = var + 1;
                        }
                        cdl.countDown();
                    }
                }.start();
            }
    
            try {
                cdl.await();  // wait for all thread return
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(var);
        }
        
    }
    

    输出:

    1259956
    

    其中的原因有可能是因为他们读取的值一样,计算的结果也一样,而我们希望的结果是一个线程的操作是基于上次计算的结果,CAS可以保证,它的执行流程如图:

    值得一提的是,“比较(Compare)”和“交换(Swap)” 这两个操作需要合并为一个原子操作并加锁,也就是当有一个线程在进行cmpxchg时其他线程需要等待,防止出现2个线程同时compare发现都ok,然后都替换,比如两个线程都对i加1,i初始化为0,它们同时读取到i为0,并计算得到结果1,并且同时进行compare发现都是前值和现值都是0,很match,然后俩线程都写入1;加上一把锁,即使这俩线程同时读取到i为0,进行compare时必须有先后顺序,就可以保证操作的一致性了。

    Java的原子类AtomicInteger等是利用CAS算法实现的,例程的CAS版:

    package juc.test.cas;
    
    import org.junit.Test;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.atomic.AtomicLong;
    
    // See also ./hotspot/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp:93
    public class AtomicTest {
    
    
        final int totalThread = 2;
        final long timesEveryThread = 1000000;
        @Test
        public void testAtomicLong(){
            AtomicLong var = new AtomicLong() ;
            CountDownLatch cdl = new CountDownLatch(totalThread);
    
            for (int j = 0; j < totalThread; j++) {
                new Thread(){
                    @Override
                    public void run() {
                        for (int i = 0; i < timesEveryThread; i++) {
                            var.getAndIncrement();
                        }
                        cdl.countDown();
                    }
                }.start();
    
            }
    
            try {
                cdl.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(var.get());
        }
    
    }
    

    输出:

    2000000
    
  • 相关阅读:
    C#中using的使用-以FileStream写入文件为例
    C#中FileStream的对比以及使用方法
    DevExpress的TextEdit、RadioGroup、ColorPickEdit设置默认值
    DevExpress的TextEdit限制输入内容的格式,比如只能输入数字
    CS中委托与事件的使用-以Winform中跨窗体传值为例
    Winforn中怎样在窗体中打开另一个窗体
    Winforn中实现ZedGraph自定义添加右键菜单项(附源码下载)
    Winform中实现ZedGraph的多条Y轴(附源码下载)
    《深入理解Java虚拟机》内存分配策略
    三十而立
  • 原文地址:https://www.cnblogs.com/oaks/p/13418202.html
Copyright © 2011-2022 走看看