我正在阅读以下关于为什么不推荐客户端锁定的部分,如下所示:
有时,程序员使用对象的锁来实现额外的
原子操作—一种称为客户端锁定的实践。考虑一下
例如,vector类,它是一个方法被同步的列表。
现在假设我们将银行余额存储在一个向量中。这是
传输方法的简单实现:
公共无效转账(矢量账户、整笔自、整笔至、整笔金额)//
{
accounts.set(from,accounts.get(from)-金额);
accounts.set(to,accounts.get(to)+金额);
system.out.println(…);
}
vector类的get和set方法是同步的,但是
这对我们没有帮助。线程完全有可能在进程中被抢占
完成对get的第一个调用后的transfer方法。另一个
然后,线程可以将不同的值存储到同一位置。然而,我们
可以劫持锁:
公共无效转账(矢量账户、整笔自、整笔至、整笔金额)
{
已同步(帐户)
{
accounts.set(from,accounts.get(from)-金额);
accounts.set(to,accounts.get(to)+金额);
}
system.out.println(…);
}
这种方法是可行的,但它完全取决于向量
类对其所有mutator方法使用内部锁。然而,这是不是
真的吗?vector类的文档没有这样做
答应我。您必须仔细研究源代码,并希望将来
版本不会引入不同步的变体。正如你所看到的,客户-
侧锁非常脆弱,一般不建议使用。”
问题:
既然transfer方法中的synchronized(accounts)已经获得了accounts内在锁,为什么依赖于vector类的方法会对其所有mutator方法使用内在锁(以粗体和斜体突出显示)?
1条答案
按热度按时间rt4zxlrg1#
如果唯一的突变
accounts
Vector
这一切都发生在transfer
方法,那就没关系了Vector
使其突变子同步。但是通过锁定与现有mutator方法相同的对象(即
Vector
),我们可以防止在Vector
.它不仅仅是由另一个线程进行的传输,可能会损坏我们的数据,而且,例如,在
to
在我们读取其余额后,在我们设置它之前。正如霍尔格所指出的,一旦一个线程发生变异并在另一个线程中读取数据,如果您想获得一致的数据视图,即使是读取操作也需要同步。
正如核心java所建议的,最好封装您想要保护的数据,例如(玩具示例)
返回数据副本执行两个功能:
我们不提供对内部数据的引用,因此客户端无法修改
ArrayList
直接,不使用我们提供的api。客户机获得帐户余额的一致快照,这样他们就可以对所有帐户进行合计,并得到在调用时有效的总数
getAccountsSnapshot
. 否则,他们在计算总数时进行的修改可能意味着他们得到了“现实生活”中从未出现过的总数。