java并发基础(六)--- 死锁与活跃性危险
来源:互联网 发布:成都机场附近住宿 知乎 编辑:程序博客网 时间:2024/06/04 18:02
原blog:http://www.cnblogs.com/HectorHou/p/6040310.html
原书第十章
一、死锁
定义:在线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L,线程AB均不会释放自己的锁,那么这两个线程将永远地等待下去
在数据库系统的设中考虑了检测死锁以及从死锁中恢复。JVM没有办法解决死锁,只能在编程和测试时注意不要让死锁发生
1、锁顺序死锁——两个线程试图以不同的顺序来获得相同的锁
解决:如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题。
2、动态的锁顺序死锁
1 A: transferMoney(myAccount, yourAccount, 10);2 B: transferMoney(yourAccount, myAccount, 20);
使用Account中包含的唯一的不可变的并且具备可比性的键值或计算HashCode作为加锁顺序的依据
3、在协作对象间发生死锁
如果在持有锁时调用某个外部方法,那么将出现活跃性问题。在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。
1 class Taxi { 2 @GuardedBy("this") private Point location, destination; 3 private final Dispatcher dispatcher; 4 5 public Taxi(Dispatcher dispatcher) { 6 this.dispatcher = dispatcher; 7 } 8 9 public synchronized Point getLocation() {10 return location;11 }12 13 public synchronized void setLocation(Point location) {14 this.location = location;15 if (location.equals(destination))16 dispatcher.notifyAvailable(this);17 }18 19 public synchronized Point getDestination() {20 return destination;21 }22 23 public synchronized void setDestination(Point destination) {24 this.destination = destination;25 }26 }27 28 class Dispatcher {29 @GuardedBy("this") private final Set<Taxi> taxis;30 @GuardedBy("this") private final Set<Taxi> availableTaxis;31 32 public Dispatcher() {33 taxis = new HashSet<Taxi>();34 availableTaxis = new HashSet<Taxi>();35 }36 37 public synchronized void notifyAvailable(Taxi taxi) {38 availableTaxis.add(taxi);39 }40 41 public synchronized Image getImage() {42 Image image = new Image();43 for (Taxi t : taxis)44 image.drawMarker(t.getLocation());45 return image;46 }47 }
taxi.setLocation(location) 和dispatcher.notifyAvailable(taxi)同时调用时会发生锁顺序死锁
4、开放调用——在调用某个方法时不需要持有锁
在程序中应尽量使用开放调用。与那些在持有锁时调用外部方法的程序相比,更易于对依赖于开放调用的程序进行死锁分析
1 class Taxi { 2 @GuardedBy("this") private Point location, destination; 3 private final Dispatcher dispatcher; 4 5 public Taxi(Dispatcher dispatcher) { 6 this.dispatcher = dispatcher; 7 } 8 9 public synchronized Point getLocation() {10 return location;11 }12 13 public synchronized void setLocation(Point location) {14 boolean reachedDestination;15 synchronized (this) {16 this.location = location;17 reachedDestination = location.equals(destination);18 }19 if (reachedDestination)20 dispatcher.notifyAvailable(this);21 }22 23 public synchronized Point getDestination() {24 return destination;25 }26 27 public synchronized void setDestination(Point destination) {28 this.destination = destination;29 }30 }31 32 @ThreadSafe33 class Dispatcher {34 @GuardedBy("this") private final Set<Taxi> taxis;35 @GuardedBy("this") private final Set<Taxi> availableTaxis;36 37 public Dispatcher() {38 taxis = new HashSet<Taxi>();39 availableTaxis = new HashSet<Taxi>();40 }41 42 public synchronized void notifyAvailable(Taxi taxi) {43 availableTaxis.add(taxi);44 }45 46 public Image getImage() {47 Set<Taxi> copy;48 synchronized (this) {49 copy = new HashSet<Taxi>(taxis);50 }51 Image image = new Image();52 for (Taxi t : copy)53 image.drawMarker(t.getLocation());54 return image;55 }56 }
5、资源死锁——两个线程分别持有彼此想要的资源而又不会释放
例:任务执行需要连接两个数据库,两个任务分别连接了其中一个数据库,而又等待彼此释放另一个数据库的资源
6、线程饥饿死锁——一个任务中提交另一个任务,并一直等待被提交任务完成
这些任务往往是产生线程饥饿死锁的主要来源,有界线程池 / 资源池与相互依赖的任务不能一起使用。
二、死锁的避免与诊断
如果一个线程每次至多只能获得一个锁,那么就不会产生锁顺序死锁。
如果必须获取多个锁,那么在设计时必须考虑锁的顺序:尽量减少潜在的加锁交互数量,将获取锁时需要遵循的协议写入正式文档并始终遵循这些文档。
1、支持定时锁
显式使用Lock类中的定时tryLock功能来代替内置锁机制,显式锁则可以指定一个超时时限,在等待超过该时间后tryLock会返回一个失败信息
- 当定时锁失败时,并不能确定是否由于死锁导致失败
即使不使用定时锁,使用能定时的锁,如果在获取锁时超时,那么可以释放当前的锁,在一段时间后再次尝试,从而消除了死锁发生的条件(在同时获取两个锁时有效)
2、通过线程转储信息来分析死锁
线程转储包括各个运行中的线程的栈追踪信息,这类似于发生异常时的栈追踪信息。线程转储还包括加锁信息,例如每个线程持有了哪些锁,在哪些栈帧中获得这些锁,以及被阻塞的线程正在等待获取哪一个锁。在生成线程转储之前,JVM将在等待关系图通过循环来找出死锁。如果发现了一个死锁,则获取相应的死锁信息,例如在死锁中涉及哪些锁和线程,以及这个锁的获取操作位于程序的哪些位置。
显示锁比在内置锁上获得的信息精确度低。内置锁与获得它们所在的线程栈帧是相关联的,而显式的Lock只与获得它的线程相关联。
三、其他活跃性危险
1、饥饿——线程由于无法访问它所需要的资源时而不能继续执行
- 例:持有锁时执行一些无法结束的结构;底优先级的任务获取不到CPU资源
- 要避免使用线程优先级,因为这会增加平台依赖性,并可能导致活跃性问题。在大多数并发应用程序中,都可以使用默认的线程优先级
2、糟糕的响应性
- 例:后台任务若为CPU密集型,将可能影响程序响应性
- 例:不良的锁管理,某个锁持有过长时间
3、活锁——线程将不断重复执行相同的操作,而且总会失败(不会阻塞线程,但也不能继续执行)
- 例:错误的事务运行失败放在队列头
- 例:多个相互协作的线程都对彼此进行响应从而修改各自的状态,并使得任何一个线程都无法继续执行(通过等待随机长度的时间和回退可以有效的避免活锁)
- java并发基础(六)--- 死锁与活跃性危险
- Java并发学习笔记(12)避免活跃度危险(死锁)
- Java并发学习笔记(12)避免活跃度危险(死锁)
- 《Java并发编程实战》---避免活跃性危险
- java并发编程实战-避免活跃性危险
- java并发编程实战:避免活跃性危险笔记
- Java并发编程实战(学习笔记九 第十章 避免活跃性危险)
- 并发编程原则与技术(五)——避免并发编程活跃度危险
- 《Java并发编程实战》第十章 避免活跃性危险 读书笔记
- 《java并发编程实战》第10章:避免活跃性危险
- 重温《并发编程实战》---避免活跃性危险
- 线程活跃性危险
- 《Java并发编程实战》读书笔记四:活跃性和性能,死锁和显示锁
- 《Java并发编程实战》读书笔记四:活跃性和性能,死锁和显示锁
- Java 线程与并发研究系列六(死锁)
- java并发2.5-活跃度与性能
- [Java Concurrency in Practice]第十章 避免活跃性危险
- Java并发编程实战(四)活跃性
- scala的intersect的一种应用场景
- 让sourceinsight支持查看makefile、kconfig以及.s代码的方法
- 快速排序
- 【面向JS--apply、call、bind】
- Linux—shell—nohup命令使用(weblogic启动命令)nohup sh run.sh
- java并发基础(六)--- 死锁与活跃性危险
- 剑指offer--合并两个排序的链表
- 面试宝典(PART II)位运算(II)X&(X-1)的几个运用
- 直流电与交流电
- 集合练习
- Python学习--持续更新
- 动态网页
- Wannafly挑战赛1A-(树形DP)
- 实验三之顺序栈