在一个很好的
article with some concurrency tips中,一个例子被优化为以下几行:
double getBalance() { Account acct = verify(name,password); synchronized(acct) { return acct.balance; } }
如果我正确理解,同步的要点是确保此线程读取的acct.balance的值是最新的,并且对acct.balance中对象的字段的任何挂起写入也都写入主内存.
这个例子让我想了一下:只是声明acct.balance(即帐户的领域平衡)是不是更有效呢?它应该更有效率,保存对acct.balance的访问的所有同步,并且不会锁定整个acct对象.我错过了什么吗?
解决方法
你是对的. volatile提供了可见性保证.同步提供保护代码段的可见性保证和序列化.对于非常简单的情况,volatile是足够的,但是使用volatile而不是同步很容易陷入困境.
如果您认为帐户有一种调整余额的方法,那么波动性就不够好
public void add(double amount) { balance = balance + amount; }
那么如果平衡是波动的,没有其他同步,我们有一个问题.如果两个线程尝试并调用add()在一起,您可能会有一个“错过”更新,以下情况发生
Thread1 - Calls add(100) Thread2 - Calls add(200) Thread1 - Read balance (0) Thread2 - Read balance (0) Thread1 - Compute new balance (0+100=100) Thread2 - Compute new balance (0+200=200) Thread1 - Write balance = 100 Thread2 - Write balance = 200 (WRONG!)
显然这是错误的,因为两个线程读取当前值并独立更新,然后将其写回(读取,计算,写入). volatile在此无助,因此您需要同步才能确保一个线程在其他线程开始之前完成整个更新.
我一般发现,如果在编写一些代码时,我认为“我可以使用volatile而不是同步”,答案可能是“是”,但是确切地确定它的时间/努力和错误的危险是不值得的利益(次要表现).
除了一个写得很好的Account类可以在内部处理所有的同步逻辑,所以呼叫者不用担心.