前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java并发编程实战系列10之避免活跃性危险

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

作者头像
JavaEdge
发布2018-04-28 16:44:43
6640
发布2018-04-28 16:44:43
举报
文章被收录于专栏:JavaEdgeJavaEdge

10.1 死锁

哲学家问题

有环

A等B,B等A

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

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

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

10.1.1 Lock ordering Deadlocks

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

代码语言:javascript
复制
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也转,那么就会发生死锁。

代码语言:javascript
复制
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里面决定锁顺序。

代码语言:javascript
复制
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都会获取两把锁,会存在两个线程按照不同的顺序获取锁的情况。

代码语言:javascript
复制
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 开放调用

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.02.22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 10.1 死锁
    • 10.1.1 Lock ordering Deadlocks
      • 10.1.1 Dynamic Lock Order Deadlocks
        • 10.1.3 在协作对象之间发生死锁Deadlocks Between Cooperating Objects
          • 10.1.4 开放调用
            • 1.0.15 资源死锁
            • 10.2 死锁的避免与诊断
              • 10.2.1 支持定时的锁
                • 10.2.2 kill -3 发信号给JVM dump线程
                • 10.3 其他活跃性危险
                  • 10.3.1 饥饿
                    • 10.3.3 活锁Livelock
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档