java并发编程实战-避免活跃性危险

1,死锁
  1.1,应用程序中,当线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L,那么这两个线程将永远地等待下去,这种情况称为死锁或者抱死。
  1.2,数据库事务中,当它检测到一组事务发生了死锁(通过在表示等待关系的有向图中搜索循环),将选择一个牺牲者放弃这个事务。
  1.3,JVM中当一组Java线程发生死锁时,这些线程永远不能再使用了。
  1.4,锁顺序死锁:如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题。
  1.5,动态的锁顺序死锁:当顺序加锁的对象是由外部作为参数传过来的,那么对于传过来的参数顺序就很难达到一致,比如一个转账的函数,通过接口传来两个账户,那么可能从A账户转到B账户,也可能从B账户转到A账户,此时就会发生死锁。此时可以通过System.identityHashCode方法,来获取对象的hashCode,根据hashCode大小来排序锁对象顺序,如果两个对象hashCode相同,可以通过“加时赛(TieBreaking)”锁来避免死锁。
  1.6,在协作对象之间发生死锁:如果在持有锁的情况下调用某个外部方法,那么将出现活跃性问题。在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。
  1.7,开放调用:如果在调用某个方法是不需要持有锁,那么这种调用被称为开放调用。通过尽可能地使用开放调用,将更易于找出那些需要获取多个锁的代码路径,因此也就更容易确保采用一致性的顺序来获得锁。

2,死锁的避免与诊断

  2.1,在使用细粒度锁的程序中,可以通过使用一种两阶段策略来检查代码中的死锁:
    2.1.1,首先,找出在什么地方将获取多个锁,确保他们在整个程序中获取锁的顺序都保持一致
    2.1.2,尽可能地使用开放调用
  2.2,使用Lock类中的定时tryLock功能来替代内置锁机制,如果锁等待时间超过超时时间,那么tryLock会返回一个失败信息。可以通过锁超时后再次尝试,从而消除死锁发生的条件。
  2.3,虽然防止死锁的主要责任在于开发人员,但是JVM仍然通过线程转储(Thread Dump)来帮助识别死锁的发生。Jvm可以帮我们做许多工作:哪些锁导致了这个问题,涉及哪些线程,它们持有哪些其他锁,以及是否间接地给其他线程带来了不利影响
3,饥饿
  3.1,当线程由于无法访问它所需要的资源而不能继续执行时,就发生了“饥饿(Starvation)”。
  3.2,引发饥饿的最常见资源时CPU时钟周期
  3.3,在Java中对应用程序中线程优先级设置不当也会导致饥饿,通常,要避免使用线程优先级,因为这会增加平台依赖性,并且可能导致活跃性问题,在大多数并发应用程序中,都可以使用默认的优先级
4,计算密集型的后台任务将对响应性造成影响,那么应该降低它们的优先级,从而提高前台程序的响应性
5,活锁

  5.1,活锁通常发生在处理事务消息的应用程序中:如果不能成功地处理某个消息,那么消息处理机制将会回滚整个事务,并将它重新方队队列的开头。这种过度的错误恢复代码,它错误地将不可修复的错误作为可修复的错误,导致线程看上去没有没有死锁,但是却也无法正确的执行下去
  5.2,要解决这种活锁问题,需要在重试机制中引入随机性。在并发应用程序中,通过等待随机长度的时间和回退可以有效地避免活锁的发生。
6,活跃性故障是一个非常严重的问题,因为当出现活跃性故障时,只能通过中止应用程序来恢复。最常见的活跃性故障就是死锁,在设计时应该避免产生锁顺序死锁:确保线程在获取多个锁时采用一致的顺序。最好的解决方案是在应用程序中始终使用开放调用。这将大大减少需要同时持有多个锁的地方,也更容易发现这些地方。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值