Java 字段读取同步和易失性同步之间的差异
在nice中,一个示例优化为以下行:Java 字段读取同步和易失性同步之间的差异,java,concurrency,synchronization,volatile,synchronized,Java,Concurrency,Synchronization,Volatile,Synchronized,在nice中,一个示例优化为以下行: double getBalance() { Account acct = verify(name, password); synchronized(acct) { return acct.balance; } } 如果我理解正确,同步的要点是确保该线程读取的acct.balance的值是最新的,并且对acct.balance中对象字段的任何挂起写入也会写入主存 这个例子让我思考了一下:仅仅将acct.balance(即类帐户的字段余额)声明
double getBalance() {
Account acct = verify(name, password);
synchronized(acct) { return acct.balance; }
}
如果我理解正确,同步的要点是确保该线程读取的acct.balance的值是最新的,并且对acct.balance中对象字段的任何挂起写入也会写入主存
这个例子让我思考了一下:仅仅将acct.balance(即类帐户的字段余额)声明为
volatile
,不是更有效吗?它应该更高效,在访问acct.balance时保存所有的同步
,并且不会锁定整个acct
对象。我遗漏了什么吗?你是对的。volatile提供了可见性保证。synchronized提供了受保护代码段的可见性保证和序列化。对于非常简单的情况,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而不是synchronized”答案很可能是“是”,但花时间/精力来确定它,以及出错的危险,是不值得的(性能不高)
另一方面,编写良好的Account类将在内部处理所有同步逻辑,因此调用者不必担心它。将Account声明为volatile会受到以下问题和限制 1.“由于其他线程看不到局部变量,声明局部变量volatile是徒劳的”此外,如果您试图在方法中声明volatile变量,在某些情况下会出现编译器错误 双重getBalance(){ volatile acct=验证(名称、密码);//不正确。。 }
私有最终静态AccountSingleton acc_singleton=新AccountSingleton() 如果多个线程正在修改和访问数据,
synchronized
保证多个线程之间的数据一致性
如果单个线程正在修改数据,而多个线程试图读取数据的最新值,请使用volatile
construct
但对于上述代码,volatile
不能保证在多个thred修改平衡时内存的一致性。使用Double
类型符合您的目的
相关问题:
你是对的,但这篇文章实际上是关于完全不同的东西——减少锁定范围。谢谢你的回答,但我实际上建议不要将acct声明为volatile,而是acct.balance——也就是类Account的字段余额。这会修改你的一些评论。我试图在这方面稍微澄清一下这个问题。