我有一个A
类型的对象数组。每个对象都有一个值和对数组中下一个对象的引用。它还有一个方法process
,用于操作对象的值以及下一个(下一个)对象的值。
public class A{
private int value;
private A next;
public void process(){
//manipulates the value of this and of next
}
}
在我的 main 方法中,我创建了多个 B
类型的线程,它们调用数组中某些对象的 process
方法。
因此我想围绕该值创建一个锁。不应允许多个线程同时操作某些对象的值。但问题是,如果我声明 process
-方法 synchronized
,那么仍然有一个线程处理 array[0]
和一个 thead 处理 >array[1]
能够操作 array[1]
的值。对 array[1].process()
的调用应该等待,直到所有 array[0].process()
和 array[1].process( )
完成。
如何在每个邻居对周围创建锁?
最佳答案
有process
获取两把锁:一把用于该对象,一把用于下一个对象。像这样的事情:
public void process() {
synchronized (this) {
synchronized (next) {
// manipulate the value of this and of next
}
}
}
这个答案应该在你的脑海中升起一个巨大的危险信号。 任何时候当你获取多个锁时,你必须说服自己它们不会死锁!
只有当两个线程尝试以不同的顺序获取锁时才会发生死锁(即,一个线程先锁 A,然后锁 B,另一个线程锁 B,然后锁 A)。在这种情况下,这种情况不会发生——前一个对象总是在下一个对象之前被锁定。
所以,当 array[0].process()
被调用时,它锁定 array[0]
和array[1]
。当array[1].process()
被调用时,它会尝试锁定 array[1]
,这将阻塞直到 array[0].process()
完成了。当发生这种情况时,它将获取array[1]
上的锁。 ,然后尝试获取 array[2]
上的锁,还有。
不过,仍然有可能陷入僵局。如果process
中的代码启动一个新线程,然后尝试调用 process
在前一个对象上,然后尝试加入该线程,它会死锁。例如,如果 array[1].process()
这样做,那么该线程将尝试获取 array[0]
的锁和array[1]
。直到 array[1].process()
才能执行此操作完成,直到线程返回才会发生——从而导致死锁。
因此,您在允许 process
时应该非常小心。 “可插入”(例如,有人传入一个接口(interface)或扩展一个方法来确定如何完成操作。通常,在锁定下执行任何类型的“外部代码”都是危险的。
但是如果 process
完全在您的控制之下,并且不会启动任何线程,那么您应该没问题,因为获取锁的顺序是恒定的。
在这种需要微妙舞蹈的情况下,获取不在 this
上的锁并不罕见。 ,但是在一个自定义对象上只是为了这个场合:
public class A {
private final Object lock = new Object();
private int value;
private A next;
public void process() {
synchronized (lock) {
synchronized (next.lock) {
// etc
}
}
}
}
这可以保护您免受有人进入并同步您的一个对象并扰乱那微妙的舞蹈。它还可以作为视觉提醒,提醒任何正在阅读代码的人,同步方面正在发生一些有趣的事情。
关于java - 同步邻居,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27259948/