首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >使用AtomicInteger时的同步

使用AtomicInteger时的同步
EN

Stack Overflow用户
提问于 2013-06-22 17:10:49
回答 5查看 7K关注 0票数 10

假设我想实现一个非常简单的Bank Account类,并且我们想要处理并发和多线程问题,

尽管balanceAtomicInteger,但将以下方法设置为synchronized是个好主意吗?

另一方面,如果我们所有的方法都是同步的,那么就不会再使用AtomicInteger了,对吧?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.concurrent.atomic.AtomicInteger;


public class Account {
    AtomicInteger balance;
    public synchronized int checkBalance(){
        return this.balance.intValue();
    }
    public synchronized void deposit(int amount){
        balance.getAndAdd(amount);
    }
    public synchronized boolean enoughFund(int a){
        if (balance.intValue() >= a)
            return true;
        return false;
    }
    public synchronized boolean transfer_funds(Account acc, int amount){ // dest : acc 
        if (enoughFund(amount)){
            withdraw(amount);
            acc.deposit(amount);
            return true;
        }
        return false;
    }
    public synchronized boolean withdraw(int amount){
        if (checkBalance() < amount)
            return false;
        balance.getAndAdd(-1 * amount);
        return true;
    }
}
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-06-22 17:16:08

将数量声明为AtomicInteger并不能防止线程在方法执行过程中被抢占(如果它不是同步的)。因此,例如,如果您的方法transfer_funds没有以任何方式进行同步,那么即使您的数量将是AtomicInteger,您也可能得到意想不到的结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public /* synchronized */ boolean transfer_funds(Account acc, int amount){ // dest : acc 
        if (enoughFund(amount)){
            withdraw(amount);  // <- thread can be preempted in the middle of method execution
            acc.deposit(amount);
            return true;
        }
        return false;
    }

这类问题被称为竞争条件。一个可能的例子是当两个线程试图从同一个账户转移资金时。当一个线程确定存在要执行信用转移的enoughFund时,该线程可以被抢占,同时其他线程可以开始从该帐户转移资金。当第一个线程再次开始处理时,它不会再次检查是否有enoughFunds来执行信用转移(他已经检查过了,但他的知识可能已经过时了),但它会进入下一行执行。这样你可能得不到一致的结果。您在所有帐户上开始时的总金额都可以更改。

在Cay Horstmann的Core Java一书中对这方面有一个非常好的解释--这是免费提供的chapter about synchronization。它详细地描述了您正在询问的几乎完全相同的问题。

票数 8
EN

Stack Overflow用户

发布于 2013-06-22 17:17:34

是的,对于两者都是,这是一个好主意,使它同步,原子是不需要的。

如果您简单地依赖于原子而不是同步,那么您可能会遇到如下问题:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    if (enoughFund(amount)){
        withdraw(amount);
        acc.deposit(amount);
        return true;
    }

因为原子只保证您的整数不会被同时访问,这意味着即使amount是由其他线程写入的,也可以保证enoughFund(amount)提供正确的值。然而,原子本身并不能保证此行获得的值与下一行代码中的值相同,因为另一个线程可以在这两行之间执行另一个原子操作,从而导致withdraw(amount);能够将余额设置为零。

票数 7
EN

Stack Overflow用户

发布于 2013-06-22 19:02:33

如果你非常想使用AtomicInteger,你可以这样写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Account {
    private final AtomicInteger balance = new AtomicInteger(0);

    public void deposit(int amount) {
        balance.getAndAdd(amount);
    }

    public boolean withdraw(int amount) {
        for (int i; i < SOME_NUMBER_OF_ATTEMPTS; ++i) {
            int currentBalance = balance.get();
            if (currentBalance < amount) return false;
            boolean updated = balance.compareAndSet(currentBalance, currentBalance - amount);
            if (updated) return true;
        }
    }

    public boolean transfer(int amount, Account recipient) {
        boolean withdrawn = withdraw(amount);
        if (withdrawn) recipient.deposit(amount);
        return withdrawn;
    }
}

这是安全的,而且它不使用锁。进行传输或提取的线程不能保证永远都会完成这一操作,但是嘿。

循环执行比较和设置的技术是一种标准技术。这就是synchronized使用的锁本身是如何实现的。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17253260

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文