继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java并发编程实战系列10之避免活跃性危险

慕桂英546537
关注TA
已关注
手记 336
粉丝 31
获赞 200

10.1 死锁

哲学家问题

有环

A等B,B等A

数据库往往可以检测和解决死锁//TODO

JVM不行,一旦死锁只有停止重启。

下面分别介绍了几种典型的死锁情况:

10.1.1 Lock ordering Deadlocks

下面是一个经典的锁顺序死锁:两个线程用不同的顺序来获得相同的锁,如果按照锁的请求顺序来请求锁,就不会发生这种循环依赖的情况。

public class LeftRightDeadlock {    private final Object left = new Object();    private final Object right = new Object();    public void leftRight() {        synchronized (left) {            synchronized (right) {
                doSomething();
            }
        }
    }    public void rightLeft() {        synchronized (right) {            synchronized (left) {
                doSomethingElse();
            }
        }
    }    void doSomething() {
    }    void doSomethingElse() {
    }
}

10.1.1 Dynamic Lock Order Deadlocks

下面的转账例子,如果一个线程X向Y转,而另外一个线程Y向X也转,那么就会发生死锁。

public class DynamicOrderDeadlock {    // Warning: deadlock-prone!
    public static void transferMoney(Account fromAccount,
                                     Account toAccount,
                                     DollarAmount amount)
            throws InsufficientFundsException {        synchronized (fromAccount) {            synchronized (toAccount) {                if (fromAccount.getBalance().compareTo(amount) < 0)                    throw new InsufficientFundsException();                else {
                    fromAccount.debit(amount);
                    toAccount.credit(amount);
                }
            }
        }
    }    static class DollarAmount implements Comparable<DollarAmount> {        // Needs implementation

        public DollarAmount(int amount) {
        }        public DollarAmount add(DollarAmount d) {            return null;
        }        public DollarAmount subtract(DollarAmount d) {            return null;
        }        public int compareTo(DollarAmount dollarAmount) {            return 0;
        }
    }    static class Account {        private DollarAmount balance;        private final int acctNo;        private static final AtomicInteger sequence = new AtomicInteger();        public Account() {
            acctNo = sequence.incrementAndGet();
        }        void debit(DollarAmount d) {
            balance = balance.subtract(d);
        }        void credit(DollarAmount d) {
            balance = balance.add(d);
        }        DollarAmount getBalance() {            return balance;
        }        int getAcctNo() {            return acctNo;
        }
    }    static class InsufficientFundsException extends Exception {
    }
}

解决办法还是顺序话锁,考虑针对两种情况取hashcode然后判断if-else里面决定锁顺序。

class Helper {            public void transfer() throws InsufficientFundsException {                if (fromAcct.getBalance().compareTo(amount) < 0)                    throw new InsufficientFundsException();                else {
                    fromAcct.debit(amount);
                    toAcct.credit(amount);
                }
            }
        }        int fromHash = System.identityHashCode(fromAcct);        int toHash = System.identityHashCode(toAcct);        if (fromHash < toHash) {            synchronized (fromAcct) {                synchronized (toAcct) {                    new Helper().transfer();
                }
            }
        } else if (fromHash > toHash) {            synchronized (toAcct) {                synchronized (fromAcct) {                    new Helper().transfer();
                }
            }
        } else {            synchronized (tieLock) {                synchronized (fromAcct) {                    synchronized (toAcct) {                        new Helper().transfer();
                    }
                }
            }
        }

10.1.3 在协作对象之间发生死锁Deadlocks Between Cooperating Objects

下面的例子setLocation和getImage都会获取两把锁,会存在两个线程按照不同的顺序获取锁的情况。

public class CooperatingDeadlock {    // Warning: deadlock-prone!
    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);
        }        public synchronized Point getDestination() {            return destination;
        }        public synchronized void setDestination(Point destination) {            this.destination = destination;
        }
    }    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 Image {        public void drawMarker(Point p) {
        }
    }
}

10.1.4 开放调用

减小锁的力度,锁不嵌套。

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;            synchronized (this) {                this.location = location;
                reachedDestination = location.equals(destination);
            }            if (reachedDestination)
                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;            synchronized (this) {
                copy = new HashSet<Taxi>(taxis);
            }
            Image image = new Image();            for (Taxi t : copy)
                image.drawMarker(t.getLocation());            return image;
        }
    }    class Image {        public void drawMarker(Point p) {
        }
    }

}

1.0.15 资源死锁

  • 数据库连接池,A持有数据库D1连接,等待与D2连接,B持有D2的连接,等待与D1连接。

  • 线程饥饿死锁,如8.1.1小节的例子。

10.2 死锁的避免与诊断

10.2.1 支持定时的锁

tryLock

10.2.2 kill -3 发信号给JVM dump线程

10.3 其他活跃性危险

10.3.1 饥饿

10.3.3 活锁Livelock

他不会阻塞线程,但是也不能继续执行,因为线程在不断的重复执行相同的操作,而且总会失败。

例如处理事务消,回滚后再次重新把任务放在队头。

又例如发送数据包,都选择1s后重试,那么总会冲突,所以可以考虑一个随机数时间间隔。



作者:芥末无疆sss
链接:https://www.jianshu.com/p/1f697e0d1566
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP