如何使用AtomicInteger改写synchronized修饰的余额原子增减操作方法
正确的AtomicInteger实现方案
首先修改balance的定义:
private AtomicInteger balance = new AtomicInteger(0); // 初始余额可按实际业务需求设置
重写后的完整executeRequest方法:
public void executeRequest(Request request) { int amount = request.getAmount(); switch (request.getType()) { case CREDIT: int currentBalance; // 循环尝试扣款,直到CAS成功或者明确余额不足 while (true) { currentBalance = balance.get(); // 先判断余额是否足够 if (currentBalance < amount) { // 余额不足直接失败,无需重试 System.out.printf(FAILURE, request, Thread.currentThread().getName(), currentBalance); break; } int newBalance = currentBalance - amount; // 尝试原子更新 if (balance.compareAndSet(currentBalance, newBalance)) { // CAS成功才打印成功结果 System.out.printf(SUCCESS, request, Thread.currentThread().getName(), newBalance); break; } // CAS失败说明余额被其他线程修改,进入下一轮循环重新判断 } break; case REPAYMENT: // 还款无前置判断,直接调用原子加法即可,返回更新后的余额 int newBalance = balance.addAndGet(amount); System.out.printf(SUCCESS, request, Thread.currentThread().getName(), newBalance); break; } }
原错误实现的问题说明
你之前的写法存在两个核心问题:
- 把打印逻辑放在了CAS操作之前,且没有区分判断结果就直接进入CAS重试,一旦CAS失败进入下一轮循环,会重复执行打印逻辑,导致异常输出。
- 没有在余额不足的场景直接终止循环,哪怕余额已经不够,仍然会尝试CAS和重试,做了很多无用操作,而且无法正确终止流程。
实现逻辑说明
- 扣款逻辑中,我们将「余额判断」和「CAS更新」放在同一个循环体内,每次CAS失败重试时,都会重新读取最新的余额做判断,完全避免了判断条件过时的问题。
- 只有余额不足或者CAS更新成功两个场景才会跳出循环,保证每一次请求只会输出一次成功/失败日志,和原synchronized实现的行为完全一致。
- 还款操作因为只需要做加法,不需要前置判断,直接调用AtomicInteger自带的
addAndGet原子方法即可,不需要额外写CAS循环。
内容的提问来源于stack exchange,提问作者Alexander Lopatin
相关产品推荐
相关产品推荐

