corejava第11版线程问题

olmpazwi  于 2021-08-20  发布在  Java
关注(0)|答案(1)|浏览(305)

我正在阅读以下关于为什么不推荐客户端锁定的部分,如下所示:
有时,程序员使用对象的锁来实现额外的
原子操作—一种称为客户端锁定的实践。考虑一下
例如,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方法使用内在锁(以粗体和斜体突出显示)?

rt4zxlrg

rt4zxlrg1#

如果唯一的突变
accounts Vector 这一切都发生在 transfer 方法,那就没关系了 Vector 使其突变子同步。
但是通过锁定与现有mutator方法相同的对象(即 Vector ),我们可以防止在 Vector .
它不仅仅是由另一个线程进行的传输,可能会损坏我们的数据,而且,例如,在 to 在我们读取其余额后,在我们设置它之前。
正如霍尔格所指出的,一旦一个线程发生变异并在另一个线程中读取数据,如果您想获得一致的数据视图,即使是读取操作也需要同步。
正如核心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 . 否则,他们在计算总数时进行的修改可能意味着他们得到了“现实生活”中从未出现过的总数。

相关问题