0

0

Java中AtomicInteger原子类使用方法

P粉602998670

P粉602998670

发布时间:2025-09-17 12:45:02

|

884人浏览过

|

来源于php中文网

原创

AtomicInteger通过CAS实现无锁原子操作,解决多线程下i++等非原子操作导致的竞态条件问题,相比synchronized避免了阻塞和上下文切换开销,在低竞争场景下性能更优。

java中atomicinteger原子类使用方法

Java中的

AtomicInteger
是一个用于在多线程环境下安全地操作整型数据的类,它通过无锁(lock-free)的原子操作来保证数据的一致性,避免了传统
synchronized
关键字或锁机制带来的性能开销和潜在死锁问题。简单来说,它能让你在并发场景下,像操作普通变量一样安全地对整数进行增减、比较并设置等操作,而不用担心数据混乱。

解决方案

在多线程编程中,我们经常需要对一个共享的计数器或状态变量进行操作。如果直接使用

int
类型,例如
count++
,这并非一个原子操作,它通常包括读取、修改、写入三个步骤。当多个线程同时执行时,就可能出现竞态条件(Race Condition),导致最终结果不正确。
AtomicInteger
正是为了解决这个问题而生。

它的核心机制是基于CAS(Compare-And-Swap,比较并交换)指令,这是一种CPU级别的原子操作。当你尝试更新

AtomicInteger
的值时,它会先比较当前值是否是你期望的旧值,如果是,则更新为新值;否则,表示其他线程已经修改了该值,当前操作会失败并重试,直到成功为止。这个过程是无锁的,因此在很多场景下比使用
synchronized
ReentrantLock
效率更高。

以下是一些

AtomicInteger
的常见使用方法和示例:

立即学习Java免费学习笔记(深入)”;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {
    // 创建一个初始值为0的AtomicInteger
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        // 示例1: 递增操作
        System.out.println("初始值: " + counter.get()); // 0

        // incrementAndGet():先递增,再返回新值
        int newValue1 = counter.incrementAndGet();
        System.out.println("递增后 (incrementAndGet): " + newValue1 + ", 当前值: " + counter.get()); // 1, 1

        // getAndIncrement():先返回旧值,再递增
        int oldValue1 = counter.getAndIncrement();
        System.out.println("递增后 (getAndIncrement): " + oldValue1 + ", 当前值: " + counter.get()); // 1, 2

        // 示例2: 递减操作
        // decrementAndGet():先递减,再返回新值
        int newValue2 = counter.decrementAndGet();
        System.out.println("递减后 (decrementAndGet): " + newValue2 + ", 当前值: " + counter.get()); // 1, 1

        // getAndDecrement():先返回旧值,再递减
        int oldValue2 = counter.getAndDecrement();
        System.out.println("递减后 (getAndDecrement): " + oldValue2 + ", 当前值: " + counter.get()); // 1, 0

        // 示例3: 加法操作
        // addAndGet(delta):将delta加到当前值上,并返回新值
        int newValue3 = counter.addAndGet(5);
        System.out.println("加5后 (addAndGet): " + newValue3 + ", 当前值: " + counter.get()); // 5, 5

        // getAndAdd(delta):返回旧值,然后将delta加到当前值上
        int oldValue3 = counter.getAndAdd(3);
        System.out.println("加3后 (getAndAdd): " + oldValue3 + ", 当前值: " + counter.get()); // 5, 8

        // 示例4: 比较并设置 (CAS)
        // compareAndSet(expectedValue, updateValue):如果当前值等于expectedValue,则更新为updateValue并返回true;否则返回false
        boolean casSuccess1 = counter.compareAndSet(8, 10); // 当前是8,期望也是8,更新为10
        System.out.println("CAS操作 (8 -> 10) 成功? " + casSuccess1 + ", 当前值: " + counter.get()); // true, 10

        boolean casSuccess2 = counter.compareAndSet(8, 12); // 当前是10,期望是8,不匹配,不更新
        System.out.println("CAS操作 (8 -> 12) 成功? " + casSuccess2 + ", 当前值: " + counter.get()); // false, 10

        // 示例5: 多线程计数器
        AtomicInteger multiThreadCounter = new AtomicInteger(0);
        int numThreads = 10;
        int incrementsPerThread = 1000;

        Thread[] threads = new Thread[numThreads];
        for (int i = 0; i < numThreads; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < incrementsPerThread; j++) {
                    multiThreadCounter.incrementAndGet();
                }
            });
            threads[i].start();
        }

        for (Thread t : threads) {
            t.join(); // 等待所有线程完成
        }

        System.out.println("多线程计数器最终值: " + multiThreadCounter.get()); // 应该总是 numThreads * incrementsPerThread = 10000
    }
}

为什么在多线程环境下,简单的
int
不够用?
AtomicInteger
如何解决并发问题?

你可能觉得,不就是个整数加减吗,有什么难的?但当多个线程同时访问并修改一个

int
变量时,问题就来了。以
i++
为例,它在底层通常被拆分为三个步骤:1. 读取
i
的当前值;2. 将
i
的值加1;3. 将新值写回
i
。设想一下,线程A读取了
i
的值为5,正准备加1;此时线程B也读取了
i
的值为5,也准备加1。线程A先完成了加1并写回6,紧接着线程B也完成了加1并写回6。结果,
i
最终是6,而不是我们期望的7。这就是典型的竞态条件,数据丢失了。

传统的解决方案是使用

synchronized
关键字或
ReentrantLock
来保护临界区,确保同一时间只有一个线程能访问和修改变量。但这引入了锁的开销,包括上下文切换、线程阻塞和唤醒等,在高并发场景下可能成为性能瓶颈,甚至可能导致死锁。

AtomicInteger
则另辟蹊径,它利用了CPU提供的CAS(Compare-And-Swap)指令。这个指令是原子性的,意味着它要么成功执行,要么不执行,不会被中断。当
AtomicInteger
执行
incrementAndGet()
这类操作时,它会尝试循环:获取当前值,计算新值,然后调用CAS指令。CAS指令会检查当前值是否仍是它之前获取的那个值(即没有被其他线程修改),如果是,就更新为新值并成功返回;如果不是,说明有其他线程捷足先登了,它会放弃当前操作,重新获取最新值,再次尝试。这个过程是“无锁”的,因为线程不会被阻塞,只是失败后重试,这被称为乐观锁或自旋锁。这种机制在低到中等竞争程度下,通常比传统锁有更好的性能表现,因为它避免了操作系统级别的线程调度开销。

仿QQ官方商城整站 for ECSHOP
仿QQ官方商城整站 for ECSHOP

ECSHOP仿QQ官方商城整站源码,基于ECSHOP V2.7.3制作。整体采用黑色。费用漂亮。适合综合,包包,首饰类商城网站使用。 安装方法:1.访问:域名/install,按照程序提示进行安装。2.登陆网站后台,然后进行数据还原。3.模板设置中,选择QQSHOW模板4.清空缓存。。。 注:还原数据后,网站后台信息:后台地址:admin后台用户名:admin后台密码:www.shopex5.co

下载

AtomicInteger
的常用方法有哪些?它们各自适用于什么场景?

AtomicInteger
提供了一系列原子操作方法,覆盖了常见的整数操作需求:

  • get()
    /
    set(int newValue)
    : 这是最基础的读写操作。
    get()
    原子地返回当前值,
    set()
    原子地将值设置为
    newValue
    。它们主要用于获取或设置变量的最新状态,比如在某个业务流程开始时初始化计数器,或在结束时读取最终结果。
  • incrementAndGet()
    /
    getAndIncrement()
    : 这两个是原子递增操作。
    • incrementAndGet()
      :先将值加1,然后返回新值。适用于需要知道递增后的最新值,比如生成下一个序列号。
    • getAndIncrement()
      :先返回当前值,然后将值加1。适用于需要使用当前值,并同时递增它,比如获取一个ID后立即为下一个ID做准备。
  • decrementAndGet()
    /
    getAndDecrement()
    : 与递增类似,这是原子递减操作。
    • decrementAndGet()
      :先将值减1,然后返回新值。
    • getAndDecrement()
      :先返回当前值,然后将值减1。
  • addAndGet(int delta)
    /
    getAndAdd(int delta)
    : 这是原子加法操作,可以指定一个增量
    delta
    • addAndGet(delta)
      :先将当前值加上
      delta
      ,然后返回新值。
    • getAndAdd(delta)
      :先返回当前值,然后将当前值加上
      delta
      。 这些方法非常适合处理计数器,比如统计某个事件发生的次数,或者在缓存中更新某个热点数据的访问量。
  • compareAndSet(int expectedValue, int updateValue)
    : 这是
    AtomicInteger
    最核心的方法,也是所有其他原子操作的基础。它尝试将当前值原子地设置为
    updateValue
    ,前提是当前值等于
    expectedValue
    。如果设置成功,返回
    true
    ;否则返回
    false
    。这个方法在实现复杂的无锁算法时非常有用,例如实现一个自旋锁、一个简单的状态机或者构建其他原子数据结构。
  • weakCompareAndSet(int expectedValue, int updateValue)
    : 这个方法与
    compareAndSet
    类似,但在某些内存模型下,它可能无法保证操作的顺序性(即不能保证发生在它之前的写操作对其他线程可见)。在大多数情况下,我们应该优先使用
    compareAndSet
    ,除非你对JMM(Java Memory Model)有深入理解,并且需要极致的性能优化,愿意承担更复杂的并发编程风险。

AtomicInteger
的性能表现如何?何时选择它而非
synchronized
ReentrantLock

AtomicInteger
的性能表现通常在低到中等竞争程度下优于传统的锁机制(如
synchronized
ReentrantLock
)。它的主要优势在于:

  1. 无锁(Lock-Free):
    AtomicInteger
    基于CAS操作,线程不会被阻塞,而是失败后重试。这意味着没有线程上下文切换的开销,也没有死锁的风险。当竞争不激烈时,线程很少需要重试,因此效率很高。
  2. 细粒度控制:
    AtomicInteger
    只针对单个变量进行原子操作,粒度非常细。而
    synchronized
    ReentrantLock
    通常会保护一个代码块,可能包含多个变量和复杂逻辑,粒度相对较粗。

然而,

AtomicInteger
并非万能药,它也有其适用场景和局限性:

何时选择

AtomicInteger

  • 简单的计数器或状态标志: 当你需要一个在多线程环境下安全递增/递减的计数器(如网站访问量、请求处理数),或者一个简单的布尔状态标志(通过
    AtomicBoolean
    ),
    AtomicInteger
    是理想选择。
  • 高并发、低竞争: 在许多线程同时尝试修改同一个变量,但每次修改的冲突概率不高的场景下,
    AtomicInteger
    的自旋重试机制能有效避免锁的开销。
  • 构建无锁数据结构:
    AtomicInteger
    是构建更复杂的无锁数据结构(如无锁队列、无锁)的基础组件之一。
  • 性能敏感的单变量操作: 当对单个整数变量的原子操作是性能瓶颈时,
    AtomicInteger
    能提供更优的性能。

何时选择

synchronized
ReentrantLock

  • 复杂临界区: 当你需要保护一个包含多个变量、复杂逻辑或需要维护多个操作原子性的代码块时,
    AtomicInteger
    就显得力不从心了。例如,更新一个账户余额不仅要修改金额,可能还要记录交易日志,这需要一个原子性的事务,而不是简单的单变量操作。
  • 高竞争、长时间持有锁: 在竞争非常激烈,或者锁需要被长时间持有的场景下,
    AtomicInteger
    的自旋重试可能会导致CPU空转,消耗大量CPU资源。此时,传统的阻塞锁(如
    ReentrantLock
    )可能更合适,因为它们会让失败的线程进入等待状态,释放CPU给其他线程。
  • 需要公平性或条件变量:
    ReentrantLock
    提供了公平锁选项,并且可以配合
    Condition
    实现更复杂的线程协作模式(如生产者-消费者模型),这些是
    AtomicInteger
    无法提供的。

总而言之,

AtomicInteger
是Java并发工具箱中一个非常实用的组件,它提供了一种高效、无锁的方式来处理单个整数变量的原子操作。理解其底层原理和适用场景,能帮助我们编写出更健壮、更高性能的并发代码。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

826

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

726

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

731

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

396

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

445

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

429

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16882

2023.08.03

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.2万人学习

C# 教程
C# 教程

共94课时 | 5.8万人学习

Java 教程
Java 教程

共578课时 | 40.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号