AtomicReference的getAndSet的一些疑问

发布于 2022-09-12 13:11:44 字数 1557 浏览 19 评论 0

场景:多线程对银行账户的金额进行扣款操作。
金额的数据类型为BigDecimal,我利用AtomicReference来处理这个并发场景。

public static void test(DecimalAccount account) {
    List<Thread> ts = new ArrayList<>();
    // 定义了1000个线程
    for (int i = 0; i < 1000; i++) {
        ts.add(new Thread(() -> {
            // 每个线程会对对帐户余额做减10操作
            account.withDraw(BigDecimal.TEN);
        }));
    }
    ts.forEach(Thread::start);
}
class DecimalAccount {
    private AtomicReference<BigDecimal> balance; // 账户余额
    
    public DecimalAccount(BigDecimal balance) {
        this.balance = new AtomicReference<>(balance);
    }
    
    public BigDecimal getBalance() {
        return balance.get(); // 获取账户余额
    }
    
    /**
     * 扣款操作
     * @param amount 扣款金额
     */
    public void withDraw(BigDecimal amount) {
        balance.getAndSet(this.balance.get().subtract(amount));
    }
}

测试:账户余额: 10000,调用test方法,test(new DecimalAccount(new BigDecimal(10000))); 期待的结果: 0,但是最后的结果并不是0。这是什么原因呢?是我getAndSet方法用的不对吗?
运行结果
如果我把withDraw()方法改为下面的形式,最后的结果就是0, 但是下面的代码实现方式不就是类似getAndSet源码的写法吗?

public void withDraw(BigDecimal amount) {
    while(true) {
        BigDecimal prev = balance.get();
        BigDecimal next = balance.get().subtract(amount);
        if (balance.compareAndSet(prev, next)) {
           break;
        }
    }
}

可能是小的哪里理解有问题,希望大佬们能帮我指点一下。跪谢!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

调妓 2022-09-19 13:11:44

第一种写法拆开比较容易发现问题

    BigDecimal b = balance.get();
    BigDecimal newBalance = b.subtract(amount);
    balance.getAndSet(newBalance);

每一行都是原子操作,三行放在一起不是原子操作,又没有锁,于是线程不安全,多线程下会有问题

第二种写法是解决方法之一,加锁也可以

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文