假设我想实现一个非常简单的Bank Account
类,并且我们想要处理并发和多线程问题,
尽管balance
为AtomicInteger
,但将以下方法设置为synchronized
是个好主意吗?
另一方面,如果我们所有的方法都是同步的,那么就不会再使用AtomicInteger
了,对吧?
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;
}
}
发布于 2013-06-22 17:16:08
将数量声明为AtomicInteger
并不能防止线程在方法执行过程中被抢占(如果它不是同步的)。因此,例如,如果您的方法transfer_funds
没有以任何方式进行同步,那么即使您的数量将是AtomicInteger
,您也可能得到意想不到的结果
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。它详细地描述了您正在询问的几乎完全相同的问题。
发布于 2013-06-22 17:17:34
是的,对于两者都是,这是一个好主意,使它同步,原子是不需要的。
如果您简单地依赖于原子而不是同步,那么您可能会遇到如下问题:
if (enoughFund(amount)){
withdraw(amount);
acc.deposit(amount);
return true;
}
因为原子只保证您的整数不会被同时访问,这意味着即使amount
是由其他线程写入的,也可以保证enoughFund(amount)
提供正确的值。然而,原子本身并不能保证此行获得的值与下一行代码中的值相同,因为另一个线程可以在这两行之间执行另一个原子操作,从而导致withdraw(amount);
能够将余额设置为零。
发布于 2013-06-22 19:02:33
如果你非常想使用AtomicInteger
,你可以这样写:
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
使用的锁本身是如何实现的。
https://stackoverflow.com/questions/17253260
复制相似问题