Java 使用AtomicInteger时的同步

Java 使用AtomicInteger时的同步,java,multithreading,concurrency,Java,Multithreading,Concurrency,假设我想要实现一个非常简单的银行账户类,我们想要关注并发性和多线程问题 即使balance是AtomicInteger,使以下方法同步是个好主意吗 另一方面,如果所有的方法都是同步的,那么原子整数就不再有用了,对吗 import java.util.concurrent.atomic.AtomicInteger; public class Account { AtomicInteger balance; public synchronized int checkBalance

假设我想要实现一个非常简单的
银行账户
类,我们想要关注并发性和多线程问题

即使
balance
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;
    }
}
导入java.util.concurrent.AtomicInteger;
公共类帐户{
原子整数平衡;
公共同步整数校验平衡(){
返回此.balance.intValue();
}
公共同步作废存款(整笔金额){
结余.取得和增加(金额);
}
公共基金(INTA){
如果(balance.intValue()>=a)
返回true;
返回false;
}
公共资金转移(账户acc,int金额){//dest:acc
如果(足够的资金(金额)){
提取(金额);
按金(金额);
返回true;
}
返回false;
}
公共提款(整笔金额){
如果(支票余额()<金额)
返回false;
结余.取得和增加(-1*金额);
返回true;
}
}

<代码> < AtomicInteger > <代码>不能阻止线程在方法执行的中间被抢占(如果它不同步)。因此,例如,如果您的方法
transfer\u 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;
    }
public/*synchronized*/boolean转账\u资金(账户acc,int金额){//dest:acc
如果(足够的资金(金额)){

提取(金额);//对两者都是,使其同步是一个好主意,并且不需要原子

如果仅依赖原子而不是同步,则可能会出现以下问题:

    if (enoughFund(amount)){
        withdraw(amount);
        acc.deposit(amount);
        return true;
    }
因为Atomic只保证您的整数不会被同时访问,这意味着
enoughFund(amount)
将保证为
金额提供正确的值,即使该值是由其他线程写入的。但是,仅原子不能保证在此行获得的值与在下一行代码中获得的值相同,因为其他线程可以在这两行之间执行另一个原子操作,resu在<代码>中提取(金额)
能够将余额设置为零以下。

原子数据类型向您承诺的全部内容是为其值提供无锁但线程安全的访问。因此,在
同步
上使用
原子整数
的一个有效原因是,您只需要保护更新操作,如

synchronized (lockObj) {
    myInt++; // slower than AtomicInteger
}

在这种情况下,
AtomicInteger.incrementAndGet()
会更快。但是,如果同步范围大于此范围,并且增量只是其中的一部分,则使用带有非原子整数的
synchronized
块(在该块中受保护)建议使用。

是的,您是正确的。
AtomicInteger
如果对对象的所有访问都是同步的(在任何给定时刻最多只有一个线程访问其内容),则不会授予任何好处

正如其他人所指出的,当您需要对该变量进行线程安全访问,并对其执行简单更新时,最好使用
AtomicInteger

在这种情况下,您有两个复合操作,
transfer\u funds
draw
。前者有三个访问,后者有两个访问

您希望这些操作本身是原子的,也就是说,在其他人看来,它们似乎是瞬间发生的,它们不能在较小的操作中分解。要实现这一点,需要同步


最后,我想留下一个(可能)有用的建议。 您应该为每个帐户分配一个唯一的标识符。您可能会问,为什么要这样做以防止死锁

假设我们有两个线程,
T1
T2
,以及两个帐户,
a1
a2

T1

a1.transfer_funds(a2, 42);
a2.transfer_funds(a1, 00101010);
T2

a1.transfer_funds(a2, 42);
a2.transfer_funds(a1, 00101010);
您可能会经历以下交错:

T1 -> a1.enoughFund(42)
T1 -> a1.withdraw(42)
T2 -> a2.enoughFund(00101010)
T2 -> a2.withdraw(00101010)
T1 -> a2.deposit(42)    // blocks on a2's monitor, because T2 already has it
T2 -> a1.deposit(00101010)    // same as above
两个线程都会无限期地等待对方,因为您的所有方法都是同步的

例如,为每个帐户分配标识符时,解决方案是:

public class Account {
    private int balance;
    private final int id;

    /* Not synchronized */
    public boolean transferFunds(Account acc, int amount) {
        if (id < acc.getId()) {
            synchronized (this) {
                synchronized (acc) {
                    return transfer(acc, amount);
                }
            }
        }
        else if (id > acc.getId()) {
            synchronized (acc) {
                synchronized (this) {
                    return transfer(acc, amount);
                }
            }
        }
        return true; // same id, transfering to self has no effect.
    }

    private boolean transfer(Account acc, int amount) {
        if (balance >= amount) {
            balance -= amount;
            // This is not synchronized, you may make it private.
            acc.depositUnsynchronized(amount);
            return true;
        }
        return false;
    }
}
公共类帐户{
私人国际收支平衡;
私有最终int id;
/*不同步*/
公共资金(账户账户账户,内部金额){
如果(idacc.getId()){
已同步(acc){
已同步(此){
退货转账(acc,金额);
}
}
}
return true;//相同id,传递给self无效。
}
私人布尔转账(账户acc、int金额){
如果(余额>=金额){
余额-=金额;
//这不是同步的,您可以将其设置为私有。
acc.存款未同步(金额);
返回true;
}
返回false;
}
}

上面以有序的方式实现锁获取,因此,无论何种情况,所有线程都将首先尝试获取id最低的帐户。如果该帐户正在进行传输,则在第一次传输结束之前不会发生其他传输。

如果您非常想使用
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;
    }
}
公共类帐户{
私有最终AtomicInteger余额=新的AtomicInteger(0);
P