JUC中原子操作类原理分析

简介: JUC中原子操作类原理分析

一、前言


java.util.concurrent 包里提供了一系列的原子性操作类,这些类都是使用CAS机制实现的,相当于使用锁实现原子性操作在性能上有了很大的提高。由于原子性操作类的原理都大致相同,所以本文主要讲解AtomicLong类的常用方法的分析。


二、原子变量操作类AtomicLong


AtomicLong是原子性递增或者递减类,其内部使用Unsafe来实现。


public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;
  //(1).获取Unsafe类实例
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //(2).存放变量value的偏移量
    private static final long valueOffset;
  //(3).native方法来判断JVM是否支持Long类型的无锁CAS
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    private static native boolean VMSupportsCS8();
    static {
        try {
          //(4).获取value在AtomicLong中的偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
  //(5).实际变量值
    private volatile long value;
    public AtomicLong(long initialValue) {
        value = initialValue;
    }


由(1)处代码,获取Unsafe类对象的方法,是采用Unsafe.getUnsafe();获取到的,因为AtomicLong类同Unsafe类一样,是在rt.jar包下的,它也是是通过BootStrap类加载器进行加载的。可以在IDE中看下AutomicLong类的位置确认一下:


20201111202405245.png


由(1)处代码,获取Unsafe类对象的方法,是采用Unsafe.getUnsafe();获取到的,因为AtomicLong类同Unsafe类一样,是在rt.jar包下的,它也是是通过BootStrap类加载器进行加载的。可以在IDE中看下AutomicLong类的位置确认一下:


20201111202405245.png


科普一下,这里的类加载器涉及到JVM的知识。关于类加载器加载与所加载的类的对应关系见下图


20201111201733295.png

代码(2)和(4)中获取value变量在AtomicLong类中的偏移量。

代码(5)中的value变量被声明为volatile,这是为了在多线程下保证内存可见性,value是具体存放计数的变量。


二、AtomicLong中的主要方法


1、递增和递减操作

  //(6).调用unsafe的getAndAddLong方法,原子性设置value值为原始值+1,返回递增后的值
    public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }
    //(7).调用unsafe的getAndAddLong方法,原子性设置value值为原始值-1,返回递减后的值
    public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }
    //(8).调用unsafe的getAndAddLong方法,原子性设置value值为原始值+1,返回原始值
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }
   //(9).调用unsafe的getAndAddLong方法,原子性设置value值为原始值-1,返回原始值
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }

上述代码内部都是通过调用Unsafe的getAndAddLong方法来实现操作,getAndAddLong方法是原子性操作,第一个参数是AtomicLong实例的引用,第二个参数为value在AtomicLong中的偏移量,第三个参数是要设置的第二个变量的值。


简单看一下getAndAddLong 方法的实现

    public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
        return var6;
    }

其中的getLongVolatile和compareAndSwapLong方法都是native方法,交给底层语言去实现。


2、boolean compareAndSet(long expect, long update)方法


看一下另外一个比较常用的compareAndSet方法:

  public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }


同样也是调用了compareAndSwapLong方法。如果原子变量中的value值等于expect,则使用update值更新value值并返回true,否则返回false。


3、多线程使用AtomicLong统计0的个数

package AtomicLongLearn;
import java.util.concurrent.atomic.AtomicLong;
/**
 * Created by Zhong Mingyi on 2020/11/12.
 */
public class AtomicLongTest {
    //(10)创建Long型原子计数器
    private static AtomicLong atomicLong = new AtomicLong();
    //(11)数据源
    private static Integer[] arr1 = new Integer[]{0,1,2,3,0,5,6,0,56,0};
    private static Integer[] arr2 = new Integer[]{10,1,2,3,0,5,6,0,56,0};
    public static void main(String[] args) throws InterruptedException {
        //(12)统计arr1中0的个数
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < arr1.length; i++) {
                if (arr1[i] == 0) {
                    atomicLong.incrementAndGet();
                }
            }
        });
        //(13)统计arr2中0的个数
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < arr2.length; i++) {
                if (arr2[i] == 0) {
                    atomicLong.incrementAndGet();
                }
            }
        });
        //(14)线程执行
        thread1.start();
        thread2.start();
        //(15)等待线程执行完毕
        thread1.join();
        thread2.join();
        System.out.println("两个数组中0的个数为:"+atomicLong.get());
    }
}

如上代码中的两个线程各自统计自所持数据中0的个数,每当找到一个0就会调用AtomicLong的原子性递增方法。


如果没有原子操作类,多线程下实现计数器需要使用比如synchronized的同步措施,会对性能有一定损耗;但原子操作类都使用基于CAS的非阻塞算法,因此会获取较好的性能。


但是在高并发情况下,AtomicLong还是会存在性能问题,JDK8提供了一个在高并发下性能更好的原子类,我们下一期再来揭晓~


三、总结


本期学习了java.util.concurrent 包里提供的原子性操作类之一——AtomicLong,它其中的主要方法均使用非阻塞算法CAS实现,这比起使用锁实现原子性操作在性能上有了很大的提升;然后我们使用了AtomicLong类来实现多线程下统计0的个数的需求,并以此更好来理解AtomicLong类;但是在高并发的下,AtomicLong类仍然有一定的性能瓶颈,JDK8中有更好的类去弥补它在高并发下性能的不足。

相关文章
|
16天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
6017 30
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
1天前
|
数据采集 人工智能 前端开发
让 Coding Agent 从黑盒到透明:阿里云 Agent 观测审计数据采集实践
AI Agent 规模化落地带来执行黑盒、行为难追溯、成本难度量三大难题。阿里云基于 OTel 标准,面向 Coding Agent、个人通用助理和框架型 Agent,推出 LoongSuite Pilot、插件及探针等无侵入采集方案,让 Agent 实现可看见、可分析、可审计、可治理。
572 135
|
11天前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1187 3
|
8天前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
990 1
|
18天前
|
人工智能 自然语言处理 供应链
|
9天前
|
人工智能 弹性计算 安全
阿里云618活动时间、活动入口、优惠活动详细解读
2026年阿里云618创新加速季已全面开启,作为年度力度最大的云产品促销活动,本次大促覆盖轻量应用服务器、ECS云服务器、GPU云服务器、数据库、AI算力、安全服务、CDN等全品类产品,推出5亿元算力补贴、新用户限时秒杀、普惠满减、企业专享、免费试用、云大使返佣等多重福利,个人开发者、中小企业、AI团队均可享受专属低价。本文将系统梳理2026年阿里云618活动的完整时间节点、官方参与入口、各类优惠细则、使用规则、热门产品推荐及实操代码,帮助用户精准参与、高效省钱,以最低成本完成上云部署。
810 5
|
9天前
|
运维
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
1442 0