Java并发编程实战 第10章 活跃性、性能与测试

过度地使用加锁,可能导致锁顺序死锁;
过渡的使用线程池和信号量来限制对资源的使用,可能导致资源死锁;

10.1 死锁

java无法从死锁恢复;
数据库设计一般可以恢复,会选择一个牺牲者放弃这个事务。

10.1.1 锁顺序死锁

最常见的活跃性问题

如果所有线程以固定的顺序来获得锁,那么程序中就不会出现锁顺序死锁问题。
right -》left
left -》right

10.1.2 动态的锁顺序死锁

有时候并不清楚是否在锁顺序上有足够的控制权来避免死锁发生。

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

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

10.1.4 开放调用

最好的解决办法

如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用。
在程序中应尽可能使用开放调用,与持有锁时调用外部方法的程序相比,更易于对依赖于开房调用的程序进行死锁分析。

10.1.5 资源死锁

多个线程相互持有彼此正等待的锁而又不释放自己已持有的锁;
线程饥饿死锁:一个任务提交另一个任务,并等待被提交的任务在单线程的Executor中执行完成。有界线程池和相互依赖的任务不能一起使用。

10.2 死锁的避免与诊断

如果一个程序每次至多只能获得一个锁,那么就不会产生锁顺序死锁。
如果必须获得多个锁,那么设计时必须考虑锁的顺序:尽量减少潜在的加锁交互数量。
细粒度锁程序中,可以使用两阶段策略来检查代码中的死锁。

10.2.1 支持定时的锁

Lock中的tryLock代替内置锁,避免永远等待下去。显示锁可指定一个超时timeout

10.2.2 通过线程转储信息来分析死锁

Thread Dump。JVM将在等待关系图中通过搜索循环来找出死锁。
Lock转储没有内置锁信息精确度高,只能和线程相关联,内置锁可以做到栈帧关联。

10.3 其他活跃性危险

除了死锁,还有饥饿、丢失信号和活锁。

10.3.1 饥饿

避免使用线程优先级,会增加平台依赖性,可能导致活跃性问题。在大多数并发应用程序中,都可以使用默认的线程优先级。

10.3.2 糟糕的响应性

不良的锁管理:长时间占有、大容器迭代、密集计算,导致其它线程等待较长。
可以降低后台任务优先级,从而提高前台程序响应性

10.3.3 活锁

活锁不会阻塞线程,但也不能继续执行,因为线程不断重复执行相同的操作,而且总会失败。
例如处理事务消息的应用程序:失败后回滚整个事务,并重新放到队列的开头。如此反复。这也称为毒药消息,这种活锁是由过度的错误恢复代码造成的:错误的将不可修复的错误作为可修复的错误。
又例如两个人相遇,让路,反反复复。

解决活锁问题:需要在重试机制中引入随机性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值