3 回答

TA贡献1821条经验 获得超4个赞
如注释中所述,对类对象进行锁定不会对该类的所有实例进行锁定,而只会对表示您的 Account 类的 Class 对象进行锁定。该锁定与帐户对象上的锁定不兼容,因此您根本没有同步操作。
锁定单个帐户对象可以在您的 for 循环(在 sumAccounts 中)内完成,但这不会阻止这样的计划发生:
- sumAccounts locks 'first' Account and reads balance (and releases lock again at end of the synchronized block taking the lock)
- system schedules a moneyTransfer() from 'first' to 'last'
- sumAccounts locks 'last' Account and reads balance, which includes the amount that was just transferred from 'first' and was already included in the sum
因此,如果您也想防止这种情况,您也需要同步帐户上的moneyTransfer()处理.class(然后,这就过时了锁定个人对象的需要)。

TA贡献1834条经验 获得超8个赞
对于这种情况,您可以使用读写锁定。传输货币方法将使用读锁定,因此可以并发执行。sumAccounts 方法将使用写锁定,因此当它执行时,不能从其他线程执行任何传输货币(或 sumAccounts)。
使用重入锁并在类级别同步这两种方法,将与您声明的行为相同,因为它们不会允许并发执行 transferMoney 方法。
示例代码:
final ReadWriteLock rwl = new ReentrantReadWriteLock();
public boolean transferMoney(Account from, Account to, int amount) {
rwl.readLock().lock();
try{
.... Your current code here
}
finally {
rwl.readLock().unlock();
}
}
public int sumAccounts(List<Account> accounts) {
rwl.writeLock().lock();
try{
// You dont need atomic integer here, because this can be executed by one thread at a time
int sum = 0;
for (Account a : accounts) {
sum += a.getBalance();
}
return sum;
}
finally {
rwl.writeLock().unlock();
}
}
此外,重入锁的公平模式往往比非公平模式执行得更慢。有关详细信息,请查看文档。
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html

TA贡献1909条经验 获得超7个赞
查看代码非常困难,因为我们无法知道同步的对象帐户是否在所有函数中都是完全相同的实例。
首先,我们必须同意余额之和金额的转移是否是两个应该同时运行的操作。
我预计在金额转移之前和之后的余额总和是相同的。
此外,您正在使用余额的总和,这是错误的。您应该在要循环访问的对象上进行同步。
现在,即使您确实在完全相同的实例中进行协调,您仍然可以有以下计划:synchronized(Account.class)
Thread-1 (transfer)
locks from
Thread-2 (sum balance)
locks first object in the list and adds the balance to the running sum and moves to next object
Thread-1
locks to (which is the object Thread-2) processed
moves money from => to
您已经在增加之前用金额相加,并且根据计划,您可以在扣除后添加金额。tofrom
问题是您要在传输中更新 2 个对象,但总和中只锁定了 1 个对象。
我的建议是:
在同一个锁上同步两种方法,并使它们串行运行
当对象进入方法时设置一些脏标志,如果设置了,则在余额之和中跳过它们,并在所有更新完成后完成总和
transfer
你为什么还要在Java中这样做?这应该在数据库中使用具有 ACID 属性的事务进行。
添加回答
举报