【发布时间】:2016-12-08 09:31:13
【问题描述】:
我最近试图围绕一些 Java 多线程概念进行思考,并且正在编写一小段代码来帮助我理解内存可见性并尽可能正确地获得同步。根据我所阅读的内容,似乎我们持有的锁的代码量越少,我们的程序就会越高效(通常)。我写了一个小类来帮助我理解我可能遇到的一些同步问题:
public class BankAccount {
private int balance_;
public BankAccount(int initialBalance) {
if (initialBalance < 300) {
throw new IllegalArgumentException("Balance needs to be at least 300");
}
balance_ = initialBalance;
}
public void deposit(int amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit has to be positive");
}
// should be atomic assignment
// copy should also be non-shared as it's on each thread's stack
int copy = balance_;
// do the work on the thread-local copy of the balance. This work should
// not be visible to other threads till below synchronization
copy += amount;
synchronized(this) {
balance_ = copy; // make the new balance visible to other threads
}
}
public void withdraw(int amount) {
// should be atomic assignment
// copy should also be non-shared as it's on each thread's stack
int copy = balance_;
if (amount > copy) {
throw new IllegalArgumentException("Withdrawal has to be <= current balance");
}
copy -= amount;
synchronized (this) {
balance_ = copy; // update the balance and make it visible to other threads.
}
}
public synchronized getBalance() {
return balance_;
}
}
请忽略 balance_ 应该是双精度而不是整数的事实。我知道原始类型的读取/赋值是原子的,除了双精度和长整数,所以为了简单起见,我选择了整数
我尝试在函数内部编写 cmets 来描述我的想法。编写此类是为了获得正确的同步以及最小化处于锁定状态的代码量。这是我的问题:
- 此代码正确吗?它会遇到任何数据/竞争条件吗?其他线程是否可以看到所有更新?
- 此代码是否与放置方法级同步一样有效?我可以想象,随着我们所做工作量的增加(这里只是一个加法/减法),它可能会导致方法级同步出现严重的性能问题。
- 能否让这段代码更高效?
【问题讨论】:
-
"balance_应该是double而不是整数" 不,建议不要使用浮点类型来表示货币。
-
始终使用 long 或 BigInteger 作为货币,从不加倍。 1.20 欧元 = 120。
-
@dit 我会说 long 或 BigDecimal。如果您使用 long 来表示美分,则 2^63 将 more 绰绰有余。例如,以美分计算的世界 GDP 仅为 2^46 左右。如果你的客户账户里的钱多于他们的账户,我会少担心 BigInteger,而多担心猖獗的通货膨胀会破坏全球经济。 ;)
标签: java multithreading