美文网首页
Atomic类&LongAdder

Atomic类&LongAdder

作者: silence_J | 来源:发表于2020-05-06 18:10 被阅读0次

    Atomic原子类

    以AtomicXXX开头的都是用CAS操作来保证线程安全的类。

    在开发工作中经常有多个线程共同访问一个值,改变一个值的场景,这时就可以使用原子类如AtomicInteger。这里面包含了int类型,这个int类型的自增count++是线程安全的,取值也是线程安全的。

    如下,不用再对m()加synchronized,输出一定为10万

    public class AtomicInteger_01 {
    
        AtomicInteger count = new AtomicInteger(0);
    
        void m() {
            for (int i=0; i<10000; i++) {
                count.incrementAndGet(); // 自增 count++
            }
        }
    
        public static void main(String[] args) {
            AtomicInteger_01 instance = new AtomicInteger_01();
            List<Thread> threads = new ArrayList<>();
            for (int i=0; i<10; i++) {
                threads.add(new Thread(instance::m, "thread-"+i));
            }
    
            threads.forEach( thread -> thread.start());
    
            threads.forEach( thread -> {
                try {
                    thread.join(); // 自增线程执行完后才执行主线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            System.out.println(instance.count);
        }
    }
    

    上面count.incrementAndGet()方法会使count自增,不需要再额外加锁,该方法内部使用了cas无锁操作,效率更高。

    LongAdder

    以上知道递增有两种方式:

    1. 定义Long类型变量,递增时加锁synchronized
    2. 定义AtomicLong类型变量,使用incrementAndGet()方法自增
    3. 除上两种外还可用LongAdder

    简单用以下程序对比三者效率:

    public class T01_increment {
    
        static Long count1 = 0L;
        static AtomicLong count2 = new AtomicLong(0L);
        static LongAdder count3 = new LongAdder();
    
        public static void main(String[] args) throws InterruptedException {
            Thread[] threads = new Thread[1000];
    
            /**
             * synchronized
             */
            final Object lock = new Object();
            // 创建一千个线程,每个线程使count++十万次
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(() -> {
                    for (int j = 0; j < 100000; j++) {
                        synchronized (lock) {
                            count1++;
                        }
                    }
                });
            }
            // 计时
            long start = System.currentTimeMillis();
            for (Thread t : threads) {
                t.start();
            }
            for (Thread t : threads) {
                t.join();
            }
            long end = System.currentTimeMillis();
            System.out.println("synchronized:" + count1 + " time " + (end - start));
    
            /**
             * AtomicLong
             */
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(() -> {
                    for (int j = 0; j < 100000; j++) {
                        count2.incrementAndGet();
                    }
                });
            }
            // 计时
            start = System.currentTimeMillis();
            for (Thread t : threads) {
                t.start();
            }
            for (Thread t : threads) {
                t.join();
            }
            end = System.currentTimeMillis();
            System.out.println("AtomicLong:" + count1 + " time " + (end - start));
    
            /**
             * LongAdder
             */
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(() -> {
                    for (int j = 0; j < 100000; j++) {
                        count3.increment();
                    }
                });
            }
            // 计时
            start = System.currentTimeMillis();
            for (Thread t : threads) {
                t.start();
            }
            for (Thread t : threads) {
                t.join();
            }
            end = System.currentTimeMillis();
            System.out.println("LongAdder:" + count1 + " time " + (end - start));
        }
        
    }
    

    执行结果:

    synchronized:100000000 time 6978
    AtomicLong:100000000 time 2098
    LongAdder:100000000 time 965
    

    效率对比很明显

    为什么Atomic原子类比加synchronized效率高?

    synchronized是要加锁的,可能它要去操作系统申请重量级锁,效率偏低。Atomic原子类内部使用了无锁cas操作,效率高。

    为什么LongAdder比Atomic原子类效率高?

    高并发下N多线程同时去操作一个变量会造成大量线程CAS失败,然后处于自旋状态,导致严重浪费CPU资源,降低了并发性。既然AtomicLong性能问题是由于过多线程同时去竞争同一个变量的更新而降低的,那么如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源,这就是LongAdder的原理。


    LongAdder原理图.png

    LongAdder则是内部维护一个Cells数组,每个Cell里面有一个初始值为0的long型变量,在同等并发量的情况下,争夺单个变量的线程会减少,这是变相的减少了争夺共享资源的并发量,另外多个线程在争夺同一个原子变量时候,如果失败并不是自旋CAS重试,而是尝试获取其他原子变量的锁,最后当获取当前值时候是把所有变量的值累加后再加上base的值返回的。

    更详细原理参考:LongAdder原理分析
    https://blog.csdn.net/jiangtianjiao/article/details/103844801/

    相关文章

      网友评论

          本文标题:Atomic类&LongAdder

          本文链接:https://www.haomeiwen.com/subject/xypwghtx.html