在进行Java Concurrency in Practice
时,我遇到了下面的代码Deadlock,它演示了cooperating objects
之间如何发生deadlock
。如果一个线程调用getImage()
,同时另一个线程调用setLocation()
则可能会由于lock ordering
而导致死锁。为了解决这个问题,我们使用Open calls
无死锁下面的 .我的怀疑在setLocation()
没有死锁中,为什么我们要在已经synchronized
的方法中再次synchronized(this)
?synchronized
方法不是已经在this
上synchronized
了吗?我们如何使用第二个示例防止死锁?
僵局
class Taxi {
@GuardedBy("this")
private Point location, destination;
private final Dispatcher dispatcher;
public Taxi(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public synchronized Point getLocation() {
return location;
}
public synchronized void setLocation(Point location) {
this.location = location;
if (location.equals(destination))
dispatcher.notifyAvailable(this);
}
}
class Dispatcher {
@GuardedBy("this")
private final Set<Taxi> taxis;
@GuardedBy("this")
private final Set<Taxi> availableTaxis;
public Dispatcher() {
taxis = new HashSet<Taxi>();
availableTaxis = new HashSet<Taxi>();
}
public synchronized void notifyAvailable(Taxi taxi) {
availableTaxis.add(taxi);
}
public synchronized Image getImage() {
Image image = new Image();
for (Taxi t : taxis)
image.drawMarker(t.getLocation());
return image;
}
}
无死锁
class Taxi {
@GuardedBy("this")
private Point location, destination;
private final Dispatcher dispatcher;
public synchronized Point getLocation() {
return location;
}
public synchronized void setLocation(Point location) {
boolean reachedDestination;
synchronized (this) {
this.location = location;
reachedDestination = location.equals(destination);
}
if (reachedDestination)
dispatcher.notifyAvailable(this);
}
}
class Dispatcher {
@GuardedBy("this")
private final Set<Taxi> taxis;
@GuardedBy("this")
private final Set<Taxi> availableTaxis;
public synchronized void notifyAvailable(Taxi taxi) {
availableTaxis.add(taxi);
}
public Image getImage() {
Set<Taxi> copy;
synchronized (this) {
copy = new HashSet<Taxi>(taxis);
}
Image image = new Image();
for (Taxi t : copy)
image.drawMarker(t.getLocation());
return image;
}
}
"无死锁"代码清单中的Taxi.setLocation
方法不应synchronized
。
在线勘误表页面显示:
p.214 在清单 10.6 中,
Taxi.setLocation
不应该是同步方法。(但是,其主体中的同步块是正确的。(在第 6 次打印中修复。
在容易发生死锁的代码中,当线程调用Taxi.setLocation
时,它首先获取Taxi
锁。在握住Taxi
锁的同时,它继续获得Dispatcher
锁。
Taxi.setLocation
┌----lock Taxi
| ┌-lock Dispatcher
│ |
| └-unlock Dispatcher
└----unlock Taxi
此模式类似于嵌套锁获取的LeftRightDeadlock
示例。
// Warning: deadlock-prone!
public void leftRight() {
synchronized (left) {
synchronized (right) {
doSomething()
}
}
}
在修订版本中,请注意Taxi
锁在获取Dispatcher
锁之前释放。
Taxi.setLocation
┌----lock Taxi
└----unlock Taxi
┌----lock Dispatcher
└----unlock Dispatcher
秒中没有死锁,因为 getImage 未同步,因此在调用 getLocation 时不会保持任何锁。您对setLocation内部的同步(this)是正确的,它没有任何用途。