dead-lock 什么是死锁?如何避免死锁详解( 七 )


例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁 。
如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1) 。
当然,死锁一般要比两个线程互相持有对方的锁这种情况要复杂的多 。
线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A 。
线程A为了检测死锁,它需要递进地检测所有被B请求的锁 。
从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着 。这是它就知道发生了死锁 。
下面是一幅关于四个线程(A,B,C和D)之间锁占有和请求的关系图 。像这样的数据结构就可以被用来检测死锁 。

dead-lock 什么是死锁?如何避免死锁详解

文章插图
 
死锁检测
那么当检测出死锁时,这些线程该做些什么呢?
一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试 。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了 。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁(编者注:原因同超时类似,不能从根本上减轻竞争) 。
一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁 。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级 。为避免这个问题,可以在死锁发生的时候设置随机的优先级 。
开放调用避免死锁在协作对象之间发生死锁的例子中,主要是因为在调用某个方法时就需要持有锁,并且在方法内部也调用了其他带锁的方法!
如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用!
我们可以这样来改造:
同步代码块最好仅被用于保护那些涉及共享状态的操作!
class CooperatingNoDeadlock {    @ThreadSafe    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) {            boolean reachedDestination;            // 加Taxi内置锁            synchronized (this) {                this.location = location;                reachedDestination = location.equals(destination);            }            // 执行同步代码块后完毕,释放锁            if (reachedDestination)                // 加Dispatcher内置锁                dispatcher.notifyAvailable(this);        }        public synchronized Point getDestination() {            return destination;        }        public synchronized void setDestination(Point destination) {            this.destination = destination;        }    }    @ThreadSafe    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 Image getImage() {            Set<Taxi> copy;            // Dispatcher内置锁            synchronized (this) {                copy = new HashSet<Taxi>(taxis);            }            // 执行同步代码块后完毕,释放锁            Image image = new Image();            for (Taxi t : copy)                // 加Taix内置锁                image.drawMarker(t.getLocation());            return image;        }    }    class Image {        public void drawMarker(Point p) {        }    }}


推荐阅读