Java中处理多线程共享变量复合操作需保证原子性,volatile不足,应据场景选synchronized、Atomic类或ReentrantLock:前者适用于简单短操作;Atomic类用于单变量无锁高性能操作;ReentrantLock支持超时、中断及条件协作。

Java 中处理多线程下共享变量的复合操作(如 count++、if (flag) { doSomething(); flag = false; }),关键在于保证该操作的原子性——即读、改、写三步不能被其他线程打断。仅靠 volatile 不够,因为它只解决可见性和重排序,不保证原子性;而简单加锁或选错工具,又可能带来性能或死锁风险。必须根据操作类型和场景选择恰当机制。
用 synchronized 保证基础复合操作的原子性
适用于大多数通用场景,尤其当操作逻辑较简单、执行时间短、且无需复杂锁控制时。它天然支持可重入,异常时自动释放锁,语义清晰可靠。
- 修饰实例方法:锁住当前对象(
this),适合保护实例变量 - 修饰静态方法:锁住类 Class 对象,适合保护静态变量
- 使用同步代码块:粒度更细,只锁定真正需要保护的代码段,避免过度阻塞
示例:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++; // 整个操作在锁保护下执行
}
// 或更精细地:
public void incrementFine() {
synchronized (this) {
count++;
}
}
}
用 Atomic 类替代简单计数类复合操作
当复合操作仅涉及单个变量的“读-改-写”(如自增、比较并交换、累加),且追求高性能、无锁化时,优先选用 java.util.concurrent.atomic 包中的类(如 AtomicInteger、AtomicBoolean)。
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 底层基于 CAS(Compare-and-Swap),避免了锁开销
- 所有方法(如
incrementAndGet()、compareAndSet())都是原子的 - 注意:它不能用于多个变量之间的协同操作(比如同时更新
a和b并保持一致性)
示例:
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // 原子完成 +1 并返回新值
}
用 ReentrantLock 实现灵活的复合逻辑控制
当复合操作包含条件判断、循环、等待通知,或需要可中断、超时、公平性等高级特性时,ReentrantLock 比 synchronized 更合适。
- 支持
tryLock(long, TimeUnit)实现带超时的获取,避免无限等待 - 支持
lockInterruptibly(),使等待中的线程能响应中断 - 可配合
Condition实现精准的线程协作(如生产者-消费者中 wait/notify 的替代)
示例(带超时的复合检查-更新):
private final ReentrantLock lock = new ReentrantLock();
private int value = 0;
public boolean updateIfLessThan(int threshold) {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
if (value < threshold) {
value = threshold;
return true;
}
return false;
} finally {
lock.unlock();
}
}
return false;
}
避免共享:把复合操作转为线程私有或局部计算
最彻底的线程安全方式是不共享。如果复合操作本质是中间计算、状态累积或资源初始化,考虑从源头消除共享。
- 将对象创建移至方法内部(如
Cipher、Key),使其成为局部变量 - 使用
ThreadLocal为每个线程提供独立副本(适合上下文对象、格式化器、数据库连接等) - 对批量数据采用分段处理(如分组计数后合并),减少临界区竞争
示例(避免 Cipher 共享):
private static byte[] decryptOfDiyIV(byte[] encryptedData, byte[] keyBytes, byte[] ivs) {
// 每次调用都新建 Cipher,不共享
Cipher cipher = Cipher.getInstance(ALGORITHM_STR, "BC");
Key key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivs));
return cipher.doFinal(encryptedData);
}

















