AtomicInteger在实际项目中的应用

本文介绍如何在高并发环境下使用AtomicInteger类优化代码性能,并通过实例演示其在多线程环境中的应用,包括如何利用ThreadLocal和过滤器记录缓存调用次数,以及AtomicInteger线程安全原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天写代码,尝试使用了AtomicInteger这个类,感觉使用起来很爽,特别适用于高并发访问,下面贴一个简单的例子:

Java代码  收藏代码
  1. CashierContext类部分代码:  
  2.   
  3. private Map<String, AtomicInteger> counter          = new HashMap<String, AtomicInteger>();  
  4.   
  5.   
  6. private void initCounter() {  
  7.      counter.put("cvm"new AtomicInteger(0));  
  8. }  
  9.   
  10. //被调用一次自动+1  
  11.   
  12. public MobileCashierViewModel getCvm() {  
  13.   
  14.         if (cvm != null) {  
  15.             counter.get("cvm").incrementAndGet();  
  16.         }  
  17.         return cvm;  
  18.     }  
 

使用场景:

因为通过WS服务获取MobileCashierViewModel 这个对象比较频繁,会很影响系统资源,可以将cvm存入缓存中,想要查看缓存cvm有多大价值,那么可以设置一个计数,来统记cvm被调用的次数

 

 

然后将CashierContext放入ThreadLocal中,然后再写一个过滤器,在过滤器里面可以得到获取这个服务从缓存中取的次数,这个就可以很容易看出来缓存价值。

 

那么为什么不使用记数器自加呢,例如count++这样的,因为这种计数是线程不安全的,高并发访问时统计会有误,而AtomicInteger为什么能够达到多而不乱,处理高并发应付自如呢,我们才看看AtomicInteger的源代码:

 

 

Java代码  收藏代码
  1. private volatile int value;  
 

 

大家可以看到有这个变量,value就是你设置的自加起始值。注意看它的访问控制符,是volatile,这个就是保证AtomicInteger线程安全的根源,熟悉并发的同学一定知道在java中处理并发主要有两种方式:

1,synchronized关键字,这个大家应当都各种面试和笔试中经常遇到。

2,volatile修饰符的使用,相信这个修饰符大家平时在项目中使用的也不是很多。

 

这里重点说一下volatile:

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫将变化的值重新写入共享内存,这样两个不同的线程在访问同一个共享变量的值时,始终看到的是同一个值。

 

java语言规范指出:为了获取最佳的运行速度,允许线程保留共享变量的副本,当这个线程进入或者离开同步代码块时,才与共享成员变量进行比对,如果有变化再更新共享成员变量。这样当多个线程同时访问一个共享变量时,可能会存在值不同步的现象。

 

而volatile这个值的作用就是告诉VM:对于这个成员变量不能保存它的副本,要直接与共享成员变量交互。

 

建议:当多个线程同时访问一个共享变量时,可以使用volatile,而当访问的变量已在synchronized代码块中时,不必使用。

缺点:使用volatile将使得VM优化失去作用,导致效率较低,所以要在必要的时候使用。


原文地址:http://haininghacker-foxmail-com.iteye.com/blog/1401346

<think>嗯,用户这次想了解Java中volatile关键字的实际应用场景和案例。之前讨论的是MySQL单表数据量限制的问题,现在突然转向并发编程,看来用户可能在开发中遇到了多线程可见性问题。 从用户提供的引用材料看,主要聚焦在volatile的作用原理和使用场景上。用户特别强调要"实际应用案例",说明ta可能已经了解基础理论,更需要落地指导。我注意到引用[3]提到"通过多个代码案例展示应用",这个很符合用户需求。 用户身份应该是Java开发者,可能在调试多线程程序时发现某些变量值"莫名其妙"地不一致。ta的深层需求可能是:如何用最轻量级的同步方案解决可见性问题?因为引用[2]提到volatile常与锁配合使用,用户可能也在权衡两者的使用成本。 需要重点区分几个经典场景:状态标志位、双重检查锁、读写分离计数。其中双重检查锁最容易被误用,必须强调JDK5后的修复方案。对于状态标志位这种高频场景,要给出立即能用的代码模板。 硬件层面的说明也不能少,因为现代CPU的多级缓存架构是可见性问题的根源。但要注意避免过于底层,用MESI协议和内存屏障的概念解释即可,重点还是落在Java代码怎么写。 用户可能还隐含比较需求:什么时候该用volatile而不用synchronized?这点在总结表格里需要明确对比。特别是要警告volatile的局限性——它不能替代真正的原子操作。 最后生成的问题要覆盖用户可能的后续探索:内存屏障实现机制、与ThreadLocal的对比、新版本Java的改进等。特别是Loom项目的影响,虽然还未发布,但前瞻性说明能体现专业深度。</think>以下是Java中`volatile`关键字的**核心应用场景**及**实际代码案例**,结合其特性(可见性、禁止指令重排)和局限性进行说明: --- ### 一、三大核心应用场景 #### 1. **状态标志位(最常用)** **场景**:多线程中安全终止线程 **原理**:`volatile`保证线程修改标志后立即可见 ```java public class WorkerThread extends Thread { private volatile boolean running = true; // 状态标志 @Override public void run() { while (running) { // 其他线程修改running后立即可见 // 执行任务 } } public void stopWork() { running = false; // 安全终止线程 } } ``` > ✅ **优势**:比`synchronized`更轻量,无锁竞争 > ⚠️ **注意**:仅适用于单写多读场景(多线程写需原子性)[^3] --- #### 2. **双重检查锁(DCL)单例模式** **场景**:延迟初始化且需线程安全 **原理**:`volatile`禁止初始化对象时的指令重排 ```java public class Singleton { private static volatile Singleton instance; // volatile禁止重排序 public static Singleton getInstance() { if (instance == null) { // 第一次检查(无锁) synchronized (Singleton.class) { // 加锁 if (instance == null) { // 第二次检查 instance = new Singleton(); // volatile禁止重排序 } // 保证初始化完成前不可见 } } return instance; } } ``` > ✅ **关键**:防止其他线程获取到**未初始化完成**的对象(JDK5+生效)[^1][^3] > ⚠️ **错误写法**:若去掉`volatile`,可能返回`instance`的半初始化状态 --- #### 3. **独立观察(Independent Observation)** **场景**:定期发布状态供其他线程读取 **原理**:写操作原子性 + 可见性 ```java public class SensorMonitor { private volatile Map<String, Double> currentReadings = new HashMap<>(); // 独立线程更新数据(单写) public void updateReading(String sensorId, double value) { Map<String, Double> newMap = new HashMap<>(currentReadings); newMap.put(sensorId, value); currentReadings = newHashMap; // volatile写(保证可见性) } // 多线程读取最新数据 public double getReading(String sensorId) { return currentReadings.get(sensorId); // volatile读 } } ``` > ✅ **适用点**:写操作频率低,读操作频繁 > ⚠️ **限制**:若需复合操作(如`putIfAbsent`)需改用并发集合[^2] --- ### 二、volatile与锁的配合场景 #### 4. **轻量级同步优化** **场景**:读多写少时减少锁竞争 **原理**:`volatile`读 + `synchronized`写 ```java public class Counter { private volatile int count; private final Object lock = new Object(); // 无锁读(利用volatile可见性) public int getCount() { return count; } // 加锁写保证原子性 public void increment() { synchronized (lock) { count++; // 写操作在锁内 } } } ``` > ✅ **性能提升**:读操作完全无锁,写操作仍安全 > ❌ **不适用**:写冲突高的场景(锁竞争无法避免)[^2] --- ### 三、volatile的局限性(不适合场景) | 场景 | 问题 | 解决方案 | |---------------------|--------------------------|-----------------------| | **复合操作** | `i++` 非原子性 | `AtomicInteger` | | **多变量约束** | 无法保证关联变量一致性 | `synchronized` | | **依赖当前值** | 如`if(count<10) count++` | 锁或`CAS` | | **写入依赖读取** | 检查后修改非原子 | 显式同步 | > ⚠️ **重要原则**:当变量值依赖于自身当前状态,或涉及多个共享变量时,必须用更强的同步机制[^1][^3]。 --- ### 四、总结:volatile适用性判断 ```mermaid graph TD A[需保证多线程可见性?] -->|No| B[无需volatile] A -->|Yes| C{操作是否原子?} C -->|单次读写| D[适用volatile] C -->|复合操作| E[需锁/CAS] D --> F{是否单写多读?} F -->|Yes| G[完美适用] F -->|多线程写| H[需原子类或锁] ``` > 📌 **核心价值**:以最低成本解决**可见性**和**重排序**问题,但**不能替代原子操作**[^1][^3]。 --- ### 相关问题 1. **`volatile`如何通过内存屏障禁止指令重排序?具体底层实现原理是什么?** 2. **在JDK 15之后的版本中,`volatile`是否有新的优化或替代方案?** 3. **`volatile`变量与`AtomicInteger`等原子类在性能上的具体差异是多少?** 4. **虚拟线程(Project Loom)的普及是否会影响`volatile`的使用场景?**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值