美文网首页JAVA并发编程实战读书笔记程序员Java学习笔记
Java并发编程实战系列10之避免活跃性危险

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

作者: 紫霞等了至尊宝五百年 | 来源:发表于2018-02-22 21:38 被阅读21次

    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后重试,那么总会冲突,所以可以考虑一个随机数时间间隔。

    相关文章

      网友评论

        本文标题:Java并发编程实战系列10之避免活跃性危险

        本文链接:https://www.haomeiwen.com/subject/sczztftx.html