java - 关于客户端锁定(同步块)的 CoreJava 第 11 版线程问题
问题描述
我正在阅读以下关于为什么不建议使用客户端锁定的部分:
“有时,程序员使用对象的锁来实现额外的
原子操作——这种做法称为客户端锁定。
例如,考虑 Vector 类,它是一个方法同步的列表。
现在假设我们存储了银行余额在 Vector 中。这是
传输方法的简单实现:
public void transfer(Vector accounts, int from, int to, int amount) //
{
accounts.set(from, accounts.get(from) - amount);
account.set(to, accounts.get(to) + amount);
System.out.println(...);
Vector
类的 get 和 set 方法是同步的,但这对
我们没有帮助。
在第一次调用 get 完成后,线程完全有可能在 transfer 方法中被抢占。然后另一个
线程可以将不同的值存储到相同的位置。但是,我们
可以劫持锁:
公共无效转移(矢量帐户,int from,int to,int amount)
{
同步(帐户)
{
accounts.set(from,accounts.get(from)-金额);
account.set(to, accounts.get(to) + amount);
}
System.out.println(...);
}
这种方法有效,但它完全取决于 Vector
类对其所有 mutator 方法使用内部锁这一事实。然而,这
真的是事实吗?Vector 类的文档没有做出这样的
承诺。您必须仔细研究源代码,并希望将来的
版本不要引入不同步的mutators。如您所见,客户
端锁定非常脆弱,通常不推荐使用。”
问题:
既然 transfer 方法中的 synchronized(accounts) 已经获得了 accounts 的内在锁,那为什么依赖于 vector 类的所有 mutator 方法都使用了内在锁(以粗斜体突出显示?
解决方案
如果 的唯一突变accounts
Vector
发生在方法中,那么同步它的突变器transfer
就无关紧要了。Vector
但是通过锁定与现有mutator方法(即 )相同的对象Vector
,我们可以防止对Vector
.
这不仅仅是由另一个线程完成的传输,它可能会破坏我们的数据,而是,例如,to
在我们读取余额之后和设置之前在账户上执行的存款。
正如 Holger 指出的那样,一旦您在一个线程中发生突变并在另一个线程中读取,如果您想要一致的数据视图,即使读取操作也需要同步。
正如Core Java所建议的那样,最好封装要保护的数据,例如(玩具示例)
public class Accounts {
private final List<Integer> accounts = new ArrayList();
public synchronized void transfer(int from, int to, int amount) {
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
}
public synchronized void deposit(int to, int amount) {
accounts.set(to, accounts.get(to) + amount);
}
public synchronized List<Integer> getAccountsSnapshot() {
// don't return our internal data structure, make a defensive copy
return new ArrayList(accounts);
}
}
返回我们的数据副本执行两个功能:
- 我们不提供对内部数据的引用,因此客户端无法
ArrayList
直接修改其中的值,而不使用我们提供的 API。 - 客户获得账户余额的一致快照,因此他们可以汇总所有账户并获得在他们调用时
getAccountsSnapshot
有效的总数。否则,在他们取总时进行修改可能意味着他们得到了一个在“现实生活”中从未发生过的总数。
推荐阅读
- python - 在 python pandas 数据框中分箱(不手动设置箱)
- python-3.x - 将 stdout 重定向到文件并在使用 python 子进程遇到 stderr 时终止
- c++ - 稀疏矩阵乘积的特征求解器
- git - 检查文件更改了多少次
- python - python中的USB条码扫描器:文本字段焦点
- android - Android 向摄像头提要添加实时叠加层
- c# - Unity3d 根据 Pitch、Roll 和 Yaw 角度列表平滑旋转对象
- windows - 远程桌面连接关闭时关闭 Windows
- pyzmq - NetMQ DealerSocket 接收消息失败
- google-chrome-extension - 提交的 Chrome 扩展程序:下载时“清单无效”