Java 为什么一般不推荐客户端锁定?
请解释康奈尔州霍茨曼(Hortsmann,Cornell)(第9版,第865页)关于“Core Java”中同步块的以下声明: Vector类的get和set方法是同步的,但这对我们没有帮助。Java 为什么一般不推荐客户端锁定?,java,multithreading,locking,synchronized,Java,Multithreading,Locking,Synchronized,请解释康奈尔州霍茨曼(Hortsmann,Cornell)(第9版,第865页)关于“Core Java”中同步块的以下声明: Vector类的get和set方法是同步的,但这对我们没有帮助。 … 但是,我们可以劫持锁: public void transfer(Vector<Double> accounts, int from, int to, int amount) { synchronized (accounts) { accounts.set(
…
但是,我们可以劫持锁:
public void transfer(Vector<Double> accounts, int from, int to, int amount)
{
synchronized (accounts)
{
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
}
//...
}
public void transfer(向量账户、整数from、整数to、整数金额)
{
已同步(帐户)
{
accounts.set(from,accounts.get(from)-金额);
accounts.set(to,accounts.get(to)+金额);
}
//...
}
这种方法是可行的,但它完全依赖于向量类对其所有mutator方法使用内在锁这一事实
为什么同步依赖于上述事实?如果一根线
拥有帐户上的锁,没有其他线程可以获得相同的锁。它不依赖于向量用于其mutator方法的锁
我能想到的唯一可能的解释是下面的一个。让线程拥有帐户的锁。如果Vector为其set/get使用了另一个锁,那么线程A必须获得一个额外的锁才能继续执行set/get,这是不可能的(一个线程可以同时持有两个不同的锁吗?)
这个解释在我看来似乎不合理,但我没有别的解释。
我错过了什么
如果线程a拥有帐户的锁,那么其他线程都无法获得相同的锁。它不依赖于向量用于其mutator方法的锁
但是如果向量使用了一个完全不相关的锁来进行自己的同步,那么你的锁对象将毫无意义。这样的代码不会同步:
x.transfer(vector, 100, 100, 100); // uses your lock
vector.add(100); // uses Vector's own, unrelated lock
如果所有代码都通过自己的方法(使用锁),并且没有人直接访问vector的方法,那么就可以了。(但是您根本不需要使用Vector的内置同步,可以使用ArrayList)
这些锁只有在所有相关代码路径都使用它们时才起作用。通常涉及多个方法,它们需要使用同一组锁正确地“相互交谈”。由程序员来确保这一点。
让线程拥有帐户的锁。如果Vector为其set/get使用了另一个锁,那么线程A必须获得一个额外的锁才能继续执行set/get,这是不可能的(一个线程可以同时持有两个不同的锁吗?)
这不是不可能的,线程A可以持有任意数量的锁。然而,在上面的访问模式中,线程A持有第一个锁是毫无意义的,因为当线程B只使用内置的向量锁时,它甚至不会尝试锁定它
这种方法是可行的,但它完全依赖于向量类对其所有mutator方法使用内在锁这一事实
这试图解释您锁定的是帐户
,而向量
锁定的是同一对象。这意味着对帐户
向量
进行更改的其他线程将被锁定,您将不会有争用条件
如果您没有共享此锁,那么您将有一个竞争条件,因为在该synchronized
块中有4个操作:
Vector
决定更改其锁定机制(是的,我知道不会),那么您的代码将具有竞争条件。更有可能的情况是,另一个程序员(或未来的您)决定将帐户
更改为不同的集合
,该集合使用不同的锁定机制,代码将中断。您不应该依赖于类的内部行为,除非它是专门记录的
如果您需要针对这种竞争条件进行保护,那么您应该对向量的访问执行同步
锁定
顺便说一句,你不应该再使用向量了。如果需要同步列表,请使用Collections.synchronizedList(新ArrayList)
或Java 5中引入的一个新并发类。我没有读过这本书,但我认为他们想说的是,每个向量
方法都是同步的(独立的),即使这样可以保护向量
不受损坏,也不能保护它可能存储在其中的信息(特别是因为业务规则、数据模型、数据结构设计或您想如何称呼它们)
示例:方法transfer
是天真地实现的,相信如果它Vector
的set
方法是同步的,那么一切都是好的
public void transfer(Vector<Double> accounts, int from, int to, int amount) {
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
}
public void transfer(向量账户、整数from、整数to、整数金额){
accounts.set(from,accounts.get(from)-金额);
accounts.set(to,accounts.get(to)+金额);
}
如果两个线程(T2和T3)同时调用传输,例如,从余额为1500美元的账户(A1)调用相同的,并将不同的传输到余额为0美元的账户(A2和A3)?例如100美元到A2,1200美元到A3
T2:accounts.get('A1')
被检索并减去100。