专栏首页JVMGC线程安全之原子性Atomic(AtomicInteger|LongAdder|AtomicLong)
原创

线程安全之原子性Atomic(AtomicInteger|LongAdder|AtomicLong)

线程安全性

当多线程访问某个类时,不管运行环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何的同步或者协同,这个类都能表现出正确的行为,那么这个类就是线程安全的.

原子性

提供互斥访问,同一时刻只有一个线程对它进行访问.

Atomic包

位于java.util.concurrent.atomic,AtomicXXX : CAS、Unsafe.compareAndSwapXXX

CAS(Compare and swap)比较和替换是设计并发算法用的的一项技术,比较和替换是用一个期望值和一个变量的当前值进行比较,如果变量的值和期望值相等,那么就用一个新值替换变量的值.

案例

线程安全

package com.keytech.task;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;

/**
 * @className: AtomicTest
 * @description: TODO 类描述
 * @author: mac
 * @date: 2020/12/27
 **/
public class AtomicTest {

    private static Integer clientTotal=5000;
    private static Integer threadTotal=200;

    private static AtomicInteger count=new AtomicInteger(0);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Semaphore semaphore= new Semaphore(threadTotal);
        for (int i = 0; i <clientTotal ; i++) {
            executorService.execute(()->{
                try{
                    semaphore.acquire();
                    update();
                    semaphore.release();
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
        System.out.println("count:"+count);
    }

    private static void update(){
        count.incrementAndGet();
    }
}

getAndAddInt源码

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
}

compareAndSwapInt(this, stateOffset, expect, update)这个方法的作用就是通过cas技术来预测stateOffset变量的初始值是否是expect,如果是,那么就把stateOffset变量的值变成update,如果不是,那么就一直自旋转,一直到stateOffset变量的初始值是expect,然后在在修改stateOffset变量的值变成update

LongAddr

线程安全

package com.keytech.task;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.LongAdder;

/**
 * @className: LongAddrTest
 * @description: TODO 类描述
 * @author: mac
 * @date: 2020/12/28
 **/
public class LongAddrTest {

    private static Integer clientTotal=5000;
    private static Integer threadTotal=200;
    
    private static LongAdder count=new LongAdder();
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Semaphore semaphore=new Semaphore(threadTotal);
        for (int i = 0; i < clientTotal; i++) {
            try{
                semaphore.acquire();
                update();
                semaphore.release();

            }catch (Exception e){
                e.printStackTrace();
            }
        }

        executorService.shutdown();
        System.out.println("count"+count);

    }
    private static void update(){
        count.increment();
    }
}

AtomicLong

线程安全

package com.keytech.task;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @className: AtomicLongTest
 * @description: TODO 类描述
 * @author: mac
 * @date: 2020/12/28
 **/
public class AtomicLongTest {

    private static Integer clientTotal=5000;
    private static Integer threadTotal=200;
    private static AtomicLong count=new AtomicLong();

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Semaphore semaphore=new Semaphore(threadTotal);
        for (int i = 0; i < clientTotal; i++) {
            try{
                semaphore.tryAcquire();
                update();
                semaphore.release();

            }catch (Exception e){
                e.printStackTrace();
            }

        }
        executorService.shutdown();
        System.out.println("count"+count);
    }

    private static void update(){
        count.incrementAndGet();
    }
}

LongAddr与AtomicLong的区别

AtomicLong的原理是依靠底层的cas来保障原子性的更新数据,在要添加或者减少的时候,会使用死循环不断地cas到特定的值,从而达到更新数据的目的。如果竞争不激烈,修改成功几率很高,否则失败概率很高,在失败几率很高的情况下,这些原子操作就会进行多次的循环操作尝试,因此性能会受到影响。

LongAdder

对于普通类型的Long和Doubble变量,JVM允许将64位的读操作或写操作拆成两个三十二位的操作。

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

LongAdder的核心是将热点数据分离,比如说它可以将AtomicLong内部核心数据value分离成一个数组,每个线程访问时,通过hash等算法,映射到其中一个数字进行计数,最终的计数结果则会这个数据的求和累加,其中热点数据value会被分离成多个cell,每个cell独自维护内部的值,当前对象实际值为所有cell累计合成,这样的话,热点就进行了有效的分离,并提高了并行度。

LongAdder在AtomicLong的基础上将单点的更新压力分散到各个节点,在低并发的时候通过对base的直接更新可以很好的保障和AtomicLong的性能基本保持一致,而在高并发的时候通过分散提高了性能。缺点是LongAdder在统计的时候如果有并发更新,可能导致统计的数据有误差。

LongAddr与AtomicLong的使用场景

实际使用中,在处理高并发时,可以优先使用LongAdder,而不是继续使用AtomicLong,当然,在线程竞争很低的情况下,使用AtomicLong更简单更实际一些,并且效率会高些。其他情况下,比如序列号生成,这种情况下需要准确的数值,全局唯一的AtomicLong才是正确的选择,而不是LongAdder

wx.jpg

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 并发编程-03线程安全性之原子性(Atomic包)及原理分析

    并发编程-06线程安全性之可见性 (synchronized + volatile)

    小小工匠
  • AtomicInteger 在高并发下性能不好,为什么?

    我们知道在 JDK1.5 中新增了并发情况下使用的 Integer/Long 所对应的原子类 AtomicInteger 和 AtomicLong。

    Rude3Knife的公众号
  • Atomic原子类的实现原理

    当我们谈论『线程安全』的时候,肯定都会想到 Atomic 类。不错,Atomic 相关类都是线程安全的,在讲 Atomic 类之前我想再聊聊『线程安全』这个概念...

    肉眼品世界
  • Java16个原子类介绍-基于JDK8

    emmmm,在写文章前我也翻阅了好多资料和书籍,其实大家在对原子类方法的使用介绍基本都没问题,但是对于java中原子类的个数是五花八门,下面我就把自己都认知和书...

    胖虎
  • 并发编程中的原子类

    对多线程访问同一个变量,我们需要加锁,而锁是比较消耗性能的,JDk1.5之后, 新增的原子操作类提供了 一种用法简单、性能高效、线程安全地更新一个变量的方式,...

    Dream城堡
  • 一篇文章快速搞懂 Atomic(原子整数/原子引用/原子数组/LongAdder)

    相信大部分开发人员,或多或少都看过或写过并发编程的代码。并发关键字除了Synchronized(如有不懂请移至传送门,[万字长文,建议收藏]关于Synchron...

    陈琛
  • 多线程编程学习八(原子操作类).

    Java 在 JDK 1.5 中提供了 java.util.concurrent.atomic 包,这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地...

    JMCui
  • 还在用Synchronized?Atomic你了解不?

    之前在学习的时候也看过AtomicInteger类很多次了,一直没有去做相关的笔记。现在遇到问题了,于是就过来写写笔记,并希望在学习的过程中解决掉问题。

    Java3y
  • Java中的原子类

    在前面的内容中,我们已经学习了CAS的原理,所以对于学习本节来说会非常容易。本节介绍Java中的原子类是java.util.concurrent.atomic包...

    胖虎
  • 高并发下的 AtomicLong 性能有点差!

    如果让你实现一个计数器,有点经验的同学可以很快的想到使用AtomicInteger或者AtomicLong进行简单的封装。

    程序员小强
  • 突击并发编程JUC系列-原子更新AtomicLong

    Java 从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、...

    山间木匠
  • Java并发编程(2)- 线程安全性详解

    说到原子性,就不得不提及JDK里的atomic包,该包中提供了很多Atomic的类,本小节主要介绍该包中常用的几个类。这些类都是通过CAS来实现原子性的,ato...

    端碗吹水
  • 【小家java】原子操作你还在用Synchronized?Atomic、LongAdder你真有必要了解一下了

    写这篇博文的原因,是因为我今天在看阿里的规范手册的时候(记录在了这里:【小家java】《阿里巴巴 Java开发手册》读后感—拥抱规范,远离伤害),发现了有一句规...

    YourBatman
  • Java并发计数器探秘

    一提到线程安全的并发计数器,AtomicLong 必然是第一个被联想到的工具。Atomic* 一系列的原子类以及它们背后的 CAS 无锁算法,常常是高性能,高并...

    kirito-moe
  • Java并发计数器探秘

    一提到线程安全的并发计数器,AtomicLong 必然是第一个被联想到的工具。Atomic* 一系列的原子类以及它们背后的 CAS 无锁算法,常常是高性能,高并...

    用户5397975
  • 理解Java轻量级并发包Atom系列工具类的设计

    他们的主要功能是提供轻量级的同步能力从而帮助我们避免内存一致性错误,从源码中观察这些工具类其设计主要利用了CAS原语+volatile的功能。我们知道volat...

    我是攻城师
  • Java 8并发教程:原子变量和ConcurrentMap

    原文地址:http://winterbe.com/posts/2015/05/22/java8-concurrency-tutorial-atomic-conc...

    用户7886150
  • (十七)AtomicInteger原子类的介绍和使用

    (十二)yield、notify、notifyAll、sleep、join、wait的区别

    HaC
  • 并发编程原理剖析——Java并发包中原子操作类原理剖析

    代码(1)通过Unsafe。getUnsafe()方法获取到Unsafe类的实例,因为AtomicLong类也是在rt.jar包下的,AtomicLong类就是...

    须臾之余

扫码关注云+社区

领取腾讯云代金券