`

《Java并发编程》之七:避免活跃性危险

    博客分类:
  • Java
阅读更多

如果所有线程以固定顺序来获取锁,那么在程序中就不会出现锁顺序死锁问题。

通过锁顺序来避免死锁:

public class InduceLockOrder {
    private static final Object tieLock = new Object();

    public void transferMoney(final Account fromAcct,
                              final Account toAcct,
                              final DollarAmount amount)
            throws InsufficientFundsException {
        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();
                    }
                }
            }
        }
    }

    interface DollarAmount extends Comparable<DollarAmount> {
    }

    interface Account {
        void debit(DollarAmount d);

        void credit(DollarAmount d);

        DollarAmount getBalance();

        int getAcctNo();
    }

    class InsufficientFundsException extends Exception {
    }
}

 

10.1.3  在协作对象之间发生的死锁

如果在持有锁时调用某个外部方法,那么将出现活跃性问题。在这个外部方法中可能会获取其他锁,或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。

如果在调用某个方法的时候不需要持有锁,那么这种调用被称为开放调用Open Call ,依赖于开放调用的类通常能表现出更好的行为,也更易于编写。

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) {
        }
    }

}

 在程序中尽量使用开放调用。与那些在持有锁的时候调用外部方法程序相比,更易于对依赖于开放调用的程序进行死锁分析。

 

10.2  死锁的避免与诊断

尽可能使用开放调用,这能极大简化死锁分析过程。

支持定时的锁:显示使用Lock类中的定时tryLock功能来代替内置锁机制。这项技术只有在同时获取两个锁时才有效,如果在嵌套方法调用中请求多个锁,还是不行。

 

10.3  其他活跃性问题

饥饿:

当线程由于无法访问它所需要的资源而不能继续执行时,就发生饥饿starvation。最常见的资源就是CUP时钟周期,比如对线程的优先级使用不当就会出现这种情况。

要避免使用java的线程优先级,因为这会增加平台依赖性,并可能导致活跃性问题。

 

糟糕的响应性:

如果一个线程长时间占有一个锁,而其他想要访问这个容器的线程就必须等待很长时间。

 

活锁:

Livelock经常发生在处理事务消息的应用程序中,如果不能成功处理某个消息,那么消息处理机制将回滚整个事务,并将它重新放到队列开头,然后循环往复的执行,又把它放到开头。

通过在重试机制中引入随机性。可以有效的避免活锁的发生。

 

小结:活跃性故障时一个非常严重的问题,因为当出现活跃性故障时,除了终止应用程序之外没有其他任何机制可以帮助从这种故障恢复过来。最常见的活跃性故障就是锁顺序死锁。在设计的时候应该避免产生锁顺序死锁,确保线程在获取多个锁时采用一致顺序,同时防止嵌套调用锁,最好使用开放调用。

 

本人博客已搬家,新地址为:http://yidao620c.github.io/

分享到:
评论

相关推荐

    Java并发编程实战

    第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第四部分 高级主题 第13章 显式锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 Java内存模型 附录A 并发性...

    Java 并发编程实战

    第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第四部分 高级主题 第13章 显式锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 Java内存模型 附录A 并发性...

    Java并发编程实战2019.zip

    Java并发编程实战,第1章 简介,第...第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第13章 显式锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 Java内存模型

    Java并发编程实践 PDF 高清版

    本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5,6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。 目录 代码清单 序 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的...

    Java并发编程实战-读书笔记

    《Java并发编程实战》个人读书笔记,非常详细: 1 简介 ...10 避免活跃性危险 11 性能与可伸缩性 12 并发程序的测试 13 显示锁 14 构建自定义的同步工具 15 原子变量与非阻塞同步机制 16 Java内存模型

    《java并发编程实战》读书笔记-第2章-线程安全性

    《java并发编程实战》读书笔记-第2章-线程安全性,脑图形式,使用xmind8制作 包括引言、线程安全性定义、原子性、加锁机制、使用锁保护状态、活跃性与性能等内容

    JAVA并发编程实践_中文版(1-16章全)_1/4

    第10章 避免活跃度危险 第11章 性能和可伸缩性 第12章 测试并发程序 第4部分 高级主题 第13章 显示锁 第14章 构建自定义的同步工具 第15章 原子变量与非阻塞同步机制 第16章 java存储模型 附录a 同步annotation 参考...

    Java并发编程(学习笔记).xmind

    Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 ...

    Java并发编程part2

    中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...

    Java并发编程实践part1

    中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...

    Java并发编程实战(中文版)

    java并发编程(中文版,pdf格式),本书共16章,主要有四部分,第一部分:基础知识,第二部分:结构化并发应用程序,第三部分:活跃性、性能与测试,第四部分:高级主题。

    java 并发编程实践 001

    java 并发编程实践001 002 两个文件全部下载后 用 7z解压 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 线程的风险 1.4 线程无处不在 第1部分 基础 第2章 线程安全 2.1 什么是线程安全性 2.2 原子...

    JAVA并发编程实践_高清

    JAVA并发编程实践 高清 pdf 带目录 良心资源 第1章 简介1 1.1 并发简史1 1.2 线程的优势2 1.2.1 发挥多处理器的强大能力2 1.2.2 建模的简单性3 1.2.3 异步事件的简化处理3 1.2.4 响应更灵敏的用户界面4 1.3...

    Java并发编程.docx

    o活跃性问题 :死锁、活锁、饥饿 o性能问题 : 使用无锁结构 :TLS,Copy-On-Write,乐观锁;Java的原子类,Disruptor无锁队列 减少锁的持有时间 :让锁 细粒 度。如ConcurrentHashmap;再如读写锁,读...

    高级java笔试题-Java-Concurrency-in-Practice:《Java并发编程实战》阅读整理笔记,附加了一些《Java并发编

    活跃性与性能 串一串 Java 并发编程的知识点 愚以为,Java 并发编程的难点在于,反常识!因为并发、并行本身,是有悖于我们大脑的工作模式的,也就是说,我们长期的写码不得不 而一旦将这一个一个的线程组合起来,...

    Android 工程师成长之路:JAVA算法的实现,数据结构 和 Android源码笔记等 分享.zip

    多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛...

    记录各种学习笔记(算法、Java、数据库、并发......).zip

    多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛...

    收集优秀的编程资源(Java为主).zip

    多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛...

Global site tag (gtag.js) - Google Analytics