news 2026/4/19 17:50:12

Java并发编程利器:Atomic原子类全解析,轻松搞定多线程安全!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java并发编程利器:Atomic原子类全解析,轻松搞定多线程安全!

文章目录

    • 一、原子类:并发世界的秩序维护者
    • 二、原子类的四大家族
      • 1. 基本类型原子类
      • 2. 数组类型原子类
      • 3. 引用类型原子类
      • 4. 字段更新器
    • 三、原子类的核心原理:CAS算法
      • CAS的工作原理
      • synchronized vs CAS
    • 四、ABA问题及解决方案
      • ABA问题的危害
      • 解决方案
    • 五、实战案例:构建线程安全的堆栈
    • 六、原子类的适用场景与注意事项
      • 适用场景
      • 注意事项
    • 七、总结
    • 参考文章

大家好,我是你们的技术老友科威舟,今天给大家分享一下Java并发编程利器Atomic原子类。

技术干货满满,一文掌握Java原子类的精髓

在Java并发编程的世界里,多线程安全是一个永恒的话题。想象一下超市排队结账的场景:如果收银系统混乱,多个顾客同时结账,结果会怎样?这就像多线程环境下的数据竞争问题。今天,我们就来聊聊Java并发包中的"排队神器"——Atomic原子类。

一、原子类:并发世界的秩序维护者

在并发编程中,我们常常遇到这样的问题:多个线程同时执行i++操作,结果可能不如预期。这是因为i++看似是一个操作,实际上包含读取、增加、写入三个步骤,在多线程环境下可能被中断。

传统的解决方案是使用synchronized关键字,但这就像在超市结账时给整个收银台加锁,虽然安全但效率低下。而Atomic原子类则提供了一种更高效的策略:它像是一位聪明的收银员,能够快速处理每个顾客的请求,确保不会混乱。

原子类的核心特点是操作不可分割,即一个操作要么完全完成,要么完全不完成,不会出现中间状态。Java的java.util.concurrent.atomic包提供了一系列原子类,让我们在不使用锁的情况下实现线程安全。

二、原子类的四大家族

Atomic原子类可以分为四大类别,每种都有其特定的使用场景。

1. 基本类型原子类

这是最常用的原子类,包括:

  • AtomicInteger:原子更新整型
  • AtomicLong:原子更新长整型
  • AtomicBoolean:原子更新布尔类型

AtomicInteger基本功能使用:

// 计数器场景AtomicIntegerrequestCount=newAtomicInteger(0);// 每个请求到来时publicvoidhandleRequest(){// 原子性增加,不会出现并发问题intcurrentCount=requestCount.incrementAndGet();// 处理请求...}

网站访问量统计场景示例:

// 网站访问量统计场景publicclassWebsiteCounter{privateAtomicIntegerpageView=newAtomicInteger(0);privateAtomicIntegeruniqueVisitor=newAtomicInteger(0);privateAtomicBooleanisWebsiteOnline=newAtomicBoolean(true);// 页面访问,多个线程可能同时调用publicvoidonPageVisit(intvisitorId){// 原子增加访问量intcurrentViews=pageView.incrementAndGet();// 使用CAS实现唯一访客统计booleansuccess=false;while(!success){intcurrentVisitors=uniqueVisitor.get();if(!hasVisitedToday(visitorId)){success=uniqueVisitor.compareAndSet(currentVisitors,currentVisitors+1);}else{break;}}System.out.println("总访问量: "+currentViews+", 独立访客: "+uniqueVisitor.get());}// 网站维护切换publicvoidtoggleMaintenanceMode(){booleanoldStatus=isWebsiteOnline.getAndSet(!isWebsiteOnline.get());System.out.println("网站状态从 "+(oldStatus?"在线":"离线")+" 切换到 "+(isWebsiteOnline.get()?"在线":"离线"));}privatebooleanhasVisitedToday(intvisitorId){// 模拟检查逻辑returnfalse;}}

2. 数组类型原子类

如果你需要原子地更新数组中的元素,可以使用:

  • AtomicIntegerArray:原子更新整型数组的元素
  • AtomicLongArray:原子更新长整型数组的元素
  • AtomicReferenceArray:原子更新引用类型数组的元素

使用示例:

// 多线程环境下的投票统计AtomicIntegerArrayvoteCounts=newAtomicIntegerArray(10);// 10个候选人// 为候选人投票publicvoidvoteForCandidate(intcandidateId){voteCounts.getAndIncrement(candidateId);}

3. 引用类型原子类

当需要原子更新对象引用时,这些类就非常有用:

  • AtomicReference:原子更新对象引用
  • AtomicStampedReference:带版本号的原子引用(解决ABA问题)
  • AtomicMarkableReference:带标记位的原子引用

AtomicReference基本使用示例:

// 缓存系统中的应用AtomicReference<Cache>cacheRef=newAtomicReference<>();publicvoidupdateCache(CachenewCache){CachecurrentCache;do{currentCache=cacheRef.get();// 只有在缓存未被其他线程修改时才会更新}while(!cacheRef.compareAndSet(currentCache,newCache));}

用户会话管理使用示例:

importjava.util.concurrent.atomic.AtomicReference;importjava.util.concurrent.atomic.AtomicStampedReference;// 用户会话管理publicclassUserSessionManager{privateAtomicReference<UserSession>currentSession=newAtomicReference<>();// 使用AtomicStampedReference解决ABA问题privateAtomicStampedReference<BankAccount>accountRef=newAtomicStampedReference<>(null,0);publicstaticclassUserSession{privateStringuserId;privateStringusername;privatelongloginTime;publicUserSession(StringuserId,Stringusername){this.userId=userId;this.username=username;this.loginTime=System.currentTimeMillis();}// getters and setters}publicstaticclassBankAccount{privateStringaccountNumber;privatedoublebalance;publicBankAccount(StringaccountNumber,doublebalance){this.accountNumber=accountNumber;this.balance=balance;}// getters and setters}// 原子性的会话切换publicbooleanswitchUserSession(UserSessionnewSession){UserSessionoldSession=currentSession.get();System.out.println("尝试从会话 "+(oldSession!=null?oldSession.userId:"null")+" 切换到 "+newSession.userId);// 使用CAS确保会话切换的原子性booleansuccess=currentSession.compareAndSet(oldSession,newSession);if(success){System.out.println("会话切换成功");}else{System.out.println("会话切换失败,可能已被其他线程修改");}returnsuccess;}// 转账操作,避免ABA问题publicbooleantransferMoney(BankAccountfrom,BankAccountto,doubleamount){int[]stampHolder=newint[1];BankAccountcurrentAccount=accountRef.get(stampHolder);intcurrentStamp=stampHolder[0];// 模拟转账逻辑BankAccountnewFromAccount=newBankAccount(from.getAccountNumber(),from.getBalance()-amount);BankAccountnewToAccount=newBankAccount(to.getAccountNumber(),to.getBalance()+amount);// 使用版本戳避免ABA问题returnaccountRef.compareAndSet(from,newFromAccount,currentStamp,currentStamp+1);}}

4. 字段更新器

当你只需要原子更新某个类的字段,而不想将整个类包装成原子类时,可以使用:

  • AtomicIntegerFieldUpdater:原子更新对象的int字段
  • AtomicLongFieldUpdater:原子更新对象的long字段
  • AtomicReferenceFieldUpdater:原子更新对象的引用字段

使用示例:

classUser{publicvolatileintage;// 必须为volatile}// 创建更新器AtomicIntegerFieldUpdater<User>ageUpdater=AtomicIntegerFieldUpdater.newUpdater(User.class,"age");Useruser=newUser();ageUpdater.set(user,30);// 原子性更新age字段

三、原子类的核心原理:CAS算法

Atomic原子类的魔法背后是CAS(Compare-And-Swap)算法,它是一种乐观锁策略。

CAS的工作原理

CAS操作包含三个参数:

  • V:需要读写的内存位置
  • A:预期的原值
  • B:想要更新的新值

CAS的伪代码实现:

booleanCAS(V,A,B){if(V==A){V=B;returntrue;// 操作成功}else{returnfalse;// 操作失败}}

当多个线程尝试使用CAS同时更新一个变量时,只有其中一个线程能成功,其他线程会失败但不会阻塞,而是可以再次尝试。

synchronized vs CAS

synchronized(悲观锁):

  • 假设最坏情况,每次操作都会冲突
  • 采用独占方式,其他线程需要阻塞等待
  • 适合竞争激烈、临界区操作复杂的场景

CAS(乐观锁):

  • 假设最好情况,操作通常不会冲突
  • 线程失败后重试,不会阻塞
  • 适合竞争不激烈、操作简单的场景

这就好比两种不同的排队策略:synchronized像是一个严格的保安,每次只允许一个人进入;CAS则像是自助服务,大家都可以尝试,如果发现冲突就重试。

四、ABA问题及解决方案

CAS算法虽然高效,但存在一个著名的ABA问题:如果一个值原来是A,变成了B,又变回A,那么CAS检查时会认为它从来没有被修改过。

ABA问题的危害

假设一个银行账户系统:

AtomicIntegeraccount=newAtomicInteger(100);// 线程1:尝试扣款50newThread(()->{intcurrent=account.get();// 模拟一些处理时间Thread.sleep(1000);// 此时账户可能经历了100→50→100的变化booleansuccess=account.compareAndSet(current,current-50);}).start();// 线程2:先扣款再充值newThread(()->{account.addAndGet(-50);// 100 → 50account.addAndGet(50);// 50 → 100}).start();

在这个例子中,线程1的CAS操作会成功,因为它检测到的值确实是100,但它不知道中间发生了100→50→100的变化。

解决方案

  1. AtomicStampedReference:通过版本号解决
AtomicStampedReference<Integer>account=newAtomicStampedReference<>(100,0);// 初始值100,版本号0// 线程1尝试扣款int[]stampHolder=newint[1];intcurrent=account.get(stampHolder);intcurrentStamp=stampHolder[0];// 只有值和版本号都匹配时才更新account.compareAndSet(current,current-50,currentStamp,currentStamp+1);
  1. AtomicMarkableReference:通过标记位解决
AtomicMarkableReference<Integer>account=newAtomicMarkableReference<>(100,false);// 使用标记位来检测变化boolean[]markHolder=newboolean[1];intcurrent=account.get(markHolder);account.compareAndSet(current,current-50,false,true);// 标记为已修改

五、实战案例:构建线程安全的堆栈

让我们通过一个实际例子来看看AtomicReference的强大之处:实现一个线程安全的堆栈。

publicclassConcurrentStack<T>{// 使用原子引用管理栈顶节点privateAtomicReference<Node<T>>top=newAtomicReference<>();// 入栈操作publicvoidpush(Titem){Node<T>newHead=newNode<>(item);Node<T>oldHead;do{oldHead=top.get();newHead.next=oldHead;}while(!top.compareAndSet(oldHead,newHead));// CAS直到成功}// 出栈操作publicTpop(){Node<T>oldHead;Node<T>newHead;do{oldHead=top.get();if(oldHead==null){returnnull;// 栈为空}newHead=oldHead.next;}while(!top.compareAndSet(oldHead,newHead));// CAS直到成功returnoldHead.item;}// 节点类privatestaticclassNode<T>{publicfinalTitem;publicNode<T>next;publicNode(Titem){this.item=item;}}}

这个实现完全无锁,依靠CAS操作保证线程安全,在高并发环境下性能优异。

六、原子类的适用场景与注意事项

适用场景

  1. 计数器:如网站访问量统计
  2. 状态标志:如系统开关控制
  3. 累积计数:如平均值计算
  4. 对象引用更新:如缓存系统

注意事项

  1. ABA问题:在重要业务场景使用带版本号的原子类
  2. 性能考量:高竞争环境下CAS频繁失败可能降低性能
  3. 单一变量:原子类保证单个变量原子性,复合操作仍需额外同步
  4. 可见性保证:字段更新器要求字段必须为volatile

七、总结

Java的Atomic原子类为我们提供了一种高效处理并发的工具。它们基于CAS机制,避免了传统锁的开销,在适当的场景下能显著提升性能。

就像交通管理一样,synchronized是红灯——所有车辆必须停止;而原子类像是环岛——车辆可以持续行驶,只在必要时调整。每种方法都有其适用场景,关键在于根据具体需求做出合适选择。

希望通过本文,你能对Java Atomic原子类有更深入的理解,并在实际项目中灵活运用,构建出高性能、线程安全的并发系统!

参考文章

  1. https://blog.csdn.net/u014207606/article/details/85107752
  2. https://blog.csdn.net/shuiziliu518/article/details/148479334
  3. https://bbs.huaweicloud.com/blogs/400010
  4. https://blog.51cto.com/u_56701/14054760
  5. https://blog.csdn.net/kalman2008/article/details/18606349
  6. https://juejin.cn/post/7327724773659394074
  7. https://blog.51cto.com/u_16175512/12935133
  • 本文主要观点基于以上参考资料,结合实际开发经验整理而成。转载请注明出处。*

更多技术干货欢迎关注微信公众号科威舟的AI笔记~

【转载须知】:转载请注明原文出处及作者信息

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/19 16:58:02

于静谧中滋养身心,让疗养成为生命的重启键

在快节奏的现代社会&#xff0c;我们似乎总在追赶——追赶工作的截止日期&#xff0c;追赶生活的既定目标&#xff0c;追赶他人的脚步&#xff0c;却唯独忘了追赶那个被忽略、被消耗的自己。当疲惫累积成难以消散的倦怠&#xff0c;当焦虑缠绕成解不开的枷锁&#xff0c;当身体…

作者头像 李华
网站建设 2026/4/19 2:08:19

汽配店用什么管理软件,汽配进销存管理系统来帮您

对于汽车配件这个行业来说&#xff0c;普通的进销存销售管理软件也免强适合&#xff0c;但总觉得少了点什么&#xff0c;因为汽车配件有一定的特殊性&#xff0c;需要管理到配件适用于哪些车型&#xff0c;汽车品牌&#xff0c;年份等数据&#xff0c;所以易特软件经过多年的开…

作者头像 李华
网站建设 2026/4/17 18:21:47

Java 爬虫对百科词条分类信息的抓取与处理

在信息爆炸的互联网时代&#xff0c;百科类平台&#xff08;如维基百科、百度百科&#xff09;沉淀了海量结构化的知识内容&#xff0c;其词条的分类体系更是梳理信息的核心脉络。利用 Java 技术构建爬虫抓取并处理百科词条的分类信息&#xff0c;不仅能为知识图谱构建、行业数…

作者头像 李华
网站建设 2026/4/19 13:28:20

2026仍考RHCE?别被“全能”标签带偏,这篇给你真实答案

2025年11月&#xff0c;有个招聘数据可把整个Linux技术圈给惊着啦。 有一家特别厉害的云计算公司&#xff0c;在招Linux运维岗位的人时&#xff0c;岗位要求里有95.1%都明确写着“有RHCE认证的优先考虑”。 而且啊&#xff0c;要是你有这个认证&#xff0c;刚开始工作的工资比没…

作者头像 李华