Java并发编程实战 - 第10章 避免活跃性危险

安全性问题是多线程开发的首要问题,为了保证线程安全,往往使用加锁机制。但过度使用锁或不正确使用锁,可能导致死锁等活跃性问题。

线程活跃性问题有:

  • 死锁
  • 饥饿
  • 槽糕的响应性
  • 活锁

死锁的原因和解决方案
最常见的活跃性问题是死锁。
死锁最常见的原因是不正确的锁顺序。

  • 锁顺序死锁
    • 原因:多个锁的循环加锁依赖,如A线程持有一个锁,等待B线程持有的另外一个锁,同时B线程正等待A线程持有的锁。
    • 解决方法:按照相同的顺序来请求锁。
  • 动态锁顺序死锁
    • 原因:锁的顺序取决于传递的参数顺序。
    • 解决方法:使用散列值(System.identityHashCode方法)和加时锁方式避免死锁
  • 协作对象之间发生死锁
    • 原因:在持有锁时调用外部方法,在外部方法中要获得另外的锁。
    • 解决方法: 开放调用 (Open Call), 即调用某个方法时不需要持有锁(使用同步块仅被用于保护涉及可变共享状态的操作)。
  • 资源死锁
    • 原因:1) 在相同的资源集合上等待; 2)线程饥饿死锁,如一个任务依赖其他任务的结果。

死锁的避免和诊断

  • 获取多锁时的顺序保持一致。
  • 使用开放调用。
  • 使用限时的显式锁 (Lock的tryLock方法)。
  • 通过线程转储信息来分析死锁。

饥饿
线程由于无法访问它所需的资源而不能继续执行,如线程的优先级使用不当,或程序进入无限循环。
避免使用线程优先级。

槽糕的响应性
将运行时间较长的线程放到后台,并降低它的优先级。

活锁
同常发生在处理事务消息的应用程序中。当多个互相协作的线程对彼此进行响应从而修改各自的状态,并使得任何一个线程无法继续执行时。
解决方法是引入随机性,如让它们分别等待一段随机时间。

示例: 通过开发调用来避免在相互协作的对象之间产生死锁。

class CooperatingNoDeadlock {
    class Taxi {
        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;
        }
    }
    class Dispatcher {
        private final Set<Taxi> taxis;
        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) {
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值