协作对象之间的死锁



在进行Java Concurrency in Practice时,我遇到了下面的代码Deadlock,它演示了cooperating objects之间如何发生deadlock。如果一个线程调用getImage(),同时另一个线程调用setLocation()则可能会由于lock ordering而导致死锁。为了解决这个问题,我们使用Open calls死锁下面的 .我的怀疑在setLocation()没有死锁中,为什么我们要在已经synchronized的方法中再次synchronized(this)synchronized方法不是已经在thissynchronized了吗?我们如何使用第二个示例防止死锁?

僵局

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)是正确的,它没有任何用途。

最新更新