字段读取和volatile的同步之间的区别

Han*_*örr 10 java concurrency synchronization volatile synchronized

一篇包含一些并发技巧的文章中,一个示例针对以下几行进行了优化:

double getBalance() {
    Account acct = verify(name, password);
    synchronized(acct) { return acct.balance; }
}
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,同步点是确保此线程读取的acct.balance的值是最新的,并且对acct.balance中对象的字段的任何挂起写入也写入主存储器.

这个例子让我想一想:将acct.balance(即Account类的字段余额)声明为不是更有效率volatile吗?它应该更有效,除了你synchronize对acct.balance的所有访问权限,并且不会锁定整个acct对象.我错过了什么吗?

Mik*_*e Q 13

你是对的.volatile提供可见性保证.synchronized提供可见性保证和受保护代码段的序列化.对于非常简单的情况,volatile就足够了,但是使用volatile而不是同步很容易遇到麻烦.

如果你假设账户有办法调整其余额,那么波动性不够好

public void add(double amount)
{
   balance = balance + amount;
}
Run Code Online (Sandbox Code Playgroud)

如果余额是易失性而没有其他同步,那么我们就会遇到问题.如果两个线程试图一起调用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!)
Run Code Online (Sandbox Code Playgroud)

显然这是错误的,因为两个线程都读取当前值并独立更新,然后将其写回(读取,计算,写入).volatile在这里没有帮助所以你需要同步以确保一个线程在另一个线程开始之前完成整个更新.

我一般会发现,如果在编写一些代码时,我认为"我可以使用volatile而不是synchronized",答案可能是"肯定",但是确定时间/努力以及确定错误的危险并不值得利益(次要表现).

另外,编写良好的Account类将在内部处理所有同步逻辑,因此调用者不必担心它.