Java concurrent Framework并发容器之concurrent.atomic包源码分析

深入理解Java并发编程中的原子类原理
本文详细解析了Java并发编程中的原子类概念,包括原子类的用途、实现原理以及关键方法如compareAndSet的运作机制。通过具体代码示例,揭示了原子类如何在多线程环境下实现线程安全的原子操作,强调了原子类在非阻塞数据结构中的重要作用。


concurrent.atomic包简介

类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可将volatile值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类,其形式如下:
  boolean compareAndSet(expectedValue, updateValue);
如果此方法(在不同的类间参数类型也不同)当前保持expectedValue,则以原子方式将变量设置为updateValue,并在成功时报告true。此包中的类还包含获取并无条件设置值的方法,以及以下描述的较弱条件的原子更新操作weakCompareAndSet。
这些方法的规范使实现能够使用当代处理器上提供的高效机器级别原子指令。但是在某些平台上,该支持可能需要某种形式的内部锁。因而,该方法不能严格保证不被阻塞 - 执行操作之前可能暂时阻塞线程。

类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference的实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。例如,类AtomicLong和AtomicInteger提供了原子增量方法。一个应用程序将按以下方式生成序列号:
class Sequencer {
  private final AtomicLong sequenceNumber = new AtomicLong(0);
  public long next() {
    return sequenceNumber.getAndIncrement();
  }
}

原子访问和更新的内存效果一般遵循以下可变规则,正如 The Java Language Specification, Third Edition (17.4 Memory Model) 中的声明:
    get具有读取volatile变量的内存效果。
    set具有写入(分配)volatile 变量的内存效果。
    除了允许使用后续(但不是以前的)内存操作,其自身不施加带有普通的非volatile写入的重新排序约束,lazySet具有写入(分配)volatile 变量的内存效果。在其他使用上下文中,当为null时(为了垃圾回收),lazySet可以应用不会再次访问的引用。
    weakCompareAndSet以原子方式读取和有条件地写入变量但不创建任何 happen-before排序,因此不提供与除weakCompareAndSet目标外任何变量以前或后续读取或写入操作有关的任何保证。
    compareAndSet和所有其他的读取和更新操作(如getAndIncrement)都有读取和写入volatile变量的内存效果。
除了包含表示单个值的类之外,此包还包含 Updater类,该类可用于获取任意选定类的任意选定volatile字段上的compareAndSet操作。AtomicReferenceFieldUpdater、AtomicIntegerFieldUpdater和AtomicLongFieldUpdater是基于反射的实用工具,可以提供对关联字段类型的访问。它们主要用于原子数据结构中,该结构中同一节点(例如,树节点的链接)的几个 volatile 字段都独立受原子更新控制。这些类在如何以及何时使用原子更新方面具有更大的灵活性,但相应的弊端是基于映射的设置较为拙笨、使用不太方便,而且在保证方面也较差。
AtomicIntegerArray、AtomicLongArray和AtomicReferenceArray类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供volatile访问语义方面也引人注目,这对于普通数组来说是不受支持的。
原子类也支持weakCompareAndSet方法,该方法具有受限制的适用性。在某些平台上,弱版本在正常情况下可能比 compareAndSet 更有效,但不同的是 weakCompareAndSet 方法的任何给定调用可能意外返回 false(即没有明确的原因)。返回 false 仅意味着可以在需要时重新尝试操作,具体取决于重复执行调用的保证,当该变量保持 expectedValue 并且没有其他线程也在尝试设置该变量时,最终将获得成功。(例如,这样的虚假失败可能是由于内存争用的结果,该争用与期望值和当前值是否相等无关)。 此外,weakCompareAndSet 不提供通常需要同步控制的排序保证。但是,在这样的更新与程序的其他 happen-before 排序不相关时,该方法可用于更新计数器和统计数据。当一个线程看到对weakCompareAndSet导致的原子变量的更新时,它不一定能看到在 weakCompareAndSet之前发生的对任何其他变量的更新。例如,在更新性能统计数据时,这也许可以接受,但其他情况几乎不可以。

AtomicMarkableReference类将单个布尔值与引用关联起来。例如,可以在数据结构内部使用此位,这意味着引用的对象在逻辑上已被删除。AtomicStampedReference类将整数值与引用关联起来。例如,这可用于表示与更新系列对应的版本号。

设计原子类主要用作各种构造块,用于实现非阻塞数据结构和相关的基础结构类。compareAndSet方法不是锁的常规替换方法。仅当对象的重要更新限定于单个变量时才应用它。

原子类不是java.lang.Integer和相关类的通用替换方法。它们不定义诸如 hashCode和compareTo之类的方法。(因为原子变量是可变的,所以对于哈希表键来说,它们不是好的选择。)另外,仅为那些通常在预期应用程序中使用的类型提供类。例如,没有表示 byte 的原子类。这种情况不常见,如果要这样做,可以使用 AtomicInteger 来保持 byte 值,并进行适当的强制转换。也可以使用 Float.floatToIntBits 和 Float.intBitstoFloat 转换来保持 float 值,使用 Double.doubleToLongBits 和 Double.longBitsToDouble 转换来保持 double 值。


对AtomicInteger源码的理解

java.util.concurrent.atomic 包提供了若干个类能实现对int,long,boolean,reference的几个特殊方法非阻塞原子性,这一系列类的主要基于以
下两点:
    private volatile int value;
    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }
1.volatile修饰变量,保证get()/set()的原子性
2.利用系统底层的CAS原语来实现非阻塞的其它方法原子操作
  private volatile int value;

    public final int getAndIncrement() {
        for (;;) {
1            int current = get();                                          
2            int next = current + 1;
3          if (compareAndSet(current, next))
                return current;
        }
    }

单看这段 代码 别说 保证原子性 即使+1 ,他都很难保证, 因为根本没有更新value的操作

 

重点在于compareAndSet() 函数

compareAndSet

public final boolean compareAndSet(int expect,
                                   int update)
如果当前值 == 预期值,则以原子方式将当前值 设置为给定的更新值
参数:
expect - 预期值
update - 新值
返回:
如果成功,则返回 true。返回 False 指示实际值与预期值不相等。

该函数 只有两个参数,可操作的确实三个值 ,即 value ,expect, update. 他使用了 由硬件保证其原子性的指令 CAS (compare and swap)。

compareAndSet  函数保证了 比较,赋值这两步操作可以通过一个原子操作完成。

然后看整个函数, 所有代码被放到了一个循环里面, 如果compareAndSet()执行失败,则说明 在int current = get(); 以后,其他线程对value进行了更新, 于是就循环一次,重新获取当前值,直到compareAndSet()执行成功为止。

 

综上,getAndIncrement() 方法并不是原子操作。 只是保证了他和其他函数对 value 值得更新都是有效的。

他所利用的是基于冲突检测的乐观并发策略。 可以想象,这种乐观在线程数目非常多的情况下,失败的概率会指数型增加。


参考

JDK 1.6  AtomicInteger

《Java并发编程实践》



/* * Copyright (c) 2025, TP-Link. All rights reserved. */ package com.tplink.cdd.vms.manager.local.domain.disorder; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.tplink.cdd.vms.common.dto.WebSocketOperationResponse; import com.tplink.cdd.vms.manager.api.web.disorder.disorderService; import com.tplink.cdd.vms.manager.core.port.eventcenter.device.DeviceBatchProcessPublisherV2; /** * Description of this file * * @author Li Tailong * @version 1.0 * @since 2025/11/3 */ @Service public class MessagePushService implements disorderService { private final ConcurrentMap<Integer, Long> sequenceRecords = new ConcurrentHashMap<>(); private final ExecutorService executor = Executors.newFixedThreadPool(10); private final ConcurrentHashMap<String, AtomicInteger> taskSnCounters = new ConcurrentHashMap<>(); @Autowired private DeviceBatchProcessPublisherV2 publisher; @Override public void asyncPush(int sequence) { executor.submit(() -> { sequenceRecords.put(sequence, System.currentTimeMillis()); // 模拟网络波动 // try { // Thread.sleep((long) (Math.random() * 100)); // } // catch (InterruptedException e) { // Thread.currentThread().interrupt(); // } WebSocketOperationResponse<ConcurrentMap<Integer, Long>> processResponse = new WebSocketOperationResponse<>(); processResponse.setData(sequenceRecords); publishOrderProgress(processResponse, "vmsId", "userId", "uuid"); }); } @Override public Map<Integer, Long> getSequenceRecords() { return new TreeMap<>(sequenceRecords); } private void publishOrderProgress(WebSocketOperationResponse<ConcurrentMap<Integer, Long>> processResponse, String vmsId, String userId, String uuid) { Integer sn = getAndIncrementSn(uuid); WebSocketOperationResponse<ConcurrentMap<Integer, Long>> wsResponse; processResponse.setSn(sn); wsResponse = WebSocketOperationResponse.processMessage(uuid, sn, processResponse.getData()); publisher.publishDeviceAddProcess(wsResponse, vmsId, userId, uuid); } private int getAndIncrementSn(String taskId) { return taskSnCounters.computeIfAbsent(taskId, k -> new AtomicInteger(-1)).incrementAndGet(); } private void removeSnCounter(String taskId) { taskSnCounters.remove(taskId); } } 把这个方法改成多线程快速发消息
11-04
rg.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'goodInfoController': Unsatisfied dependency expressed through field 'inventoryTransactionsService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'inventoryTransactionsServiceImpl': Unsatisfied dependency expressed through field 'orderTimeTask'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderTimeTask': Invocation of init method failed; nested exception is java.lang.NullPointerException at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.10.jar:5.3.10] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.10.jar:5.3.10] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.5.jar:2.5.5] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) [spring-boot-2.5.5.jar:2.5.5] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) [spring-boot-2.5.5.jar:2.5.5] at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) [spring-boot-2.5.5.jar:2.5.5] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) [spring-boot-2.5.5.jar:2.5.5] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) [spring-boot-2.5.5.jar:2.5.5] at com.cg.StoreManagmentApplication.main(StoreManagmentApplication.java:13) [classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91] at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.5.5.jar:2.5.5] Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'inventoryTransactionsServiceImpl': Unsatisfied dependency expressed through field 'orderTimeTask'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderTimeTask': Invocation of init method failed; nested exception is java.lang.NullPointerException at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.10.jar:5.3.10] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657) ~[spring-beans-5.3.10.jar:5.3.10] ... 25 common frames omitted package com.cg.task; import com.cg.mapper.OrderTimeRecordMapper; import com.cg.pojo.OrderTimeRecord; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.atomic.AtomicInteger; @Service @EnableScheduling public class OrderTimeTask { private final AtomicInteger dailyCounter = new AtomicInteger(1); private volatile LocalDateTime currentDate; private OrderTimeRecordMapper orderTimeRecordMapper; public OrderTimeTask(@Lazy OrderTimeRecordMapper orderTimeRecordMapper){ this.orderTimeRecordMapper= orderTimeRecordMapper; } // 初始化时从数据库加载 @PostConstruct public void init() { // 假设我们使用id=1的记录 OrderTimeRecord status = orderTimeRecordMapper.selectByPrimaryKey("1"); if (status != null) { dailyCounter.set(Integer.parseInt(status.getNumber())); currentDate = status.getTimeOfDay(); System.out.println("从数据库初始化: 计数器=" + dailyCounter.get() + ", 日期=" + currentDate); } else if(!status.getTimeOfDay().equals(LocalDateTime.now())){ updateDatabase(); } else { // 如果没有记录,则初始化默认值并新增到数据库 currentDate = LocalDateTime.now(); dailyCounter.set(1); OrderTimeRecord orderTimeRecord = new OrderTimeRecord(); status.setId("1"); status.setTimeOfDay(currentDate); status.setNumber(String.valueOf(dailyCounter.get())); orderTimeRecordMapper.insertSelective(orderTimeRecord); System.out.println("使用默认初始化值"); // 注意:这里也可以插入一条新记录,但根据表设计,我们假设已有记录 } } // 每天00:00重置计数器(内存和数据库) @Scheduled(cron = "0 0 0 * * ?") public void resetCounterAtMidnight() { dailyCounter.set(1); currentDate = LocalDateTime.now(); updateDatabase(); // 同步到数据库 System.out.println("[午夜任务] " + LocalDateTime.now() + " | 计数器重置: " + dailyCounter.get()); } // 更新数据库 @Transactional void updateDatabase() { OrderTimeRecord status = new OrderTimeRecord(); status.setId("1"); status.setTimeOfDay(currentDate); status.setNumber(String.valueOf(dailyCounter.get())); orderTimeRecordMapper.updateByPrimaryKey(status); } public String getCurrentDate() { return currentDate.format(DateTimeFormatter.ISO_DATE); } public String getStatus() { return String.format("计数器: %d, 当前日期: %s", dailyCounter.get(), currentDate); } }
最新发布
11-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值