Java并发之其他的活跃性危险(饥饿,活锁)

一.概述

除了死锁之外,在并发程序中还存在一些其他的活跃性危险,包括:饥饿,活锁等等。
下面我们分别了解和学习下。

二.具体学习

1.饥饿
当线程由于无法访问它所需要的资源而不能继续执行时,就会发生 “饥饿(Starvation)” .

1)引发饥饿的最常见资源就是CPU时钟周期。如果在java应用程序中对线程的优先级使用不当,或者在持有锁时执行一些无法结束的结构(例如无限循环或者无限制的等待某个资源),那么也可能导致饥饿,因为其他需要这个锁的线程将无法得到它。

2)在Thread API 中定义的线程优先级只是作为线程调度的参考。 在Thread API中定义了10个优先级,JVM根据需要将它们映射到操作系统的调度优先级,这种映射是特定平台相关的,因此在某个操作系统中两个不同的Java优先级可能被映射到同一个优先级,而在另一个操作系统中则可能被映射到另一个不同的优先级。

注意:在某些操作系统中,如果优先级的数量少于10个,那么有多个java优先级将被映射到同一个优先级。

3)操作系统的线程调度会尽力提供公平的,活跃性良好的调度方式,甚至超出了java语言规范的需求范围。 在绝大多数java应用程序中,所有线程都具有相同的优先级(Thread.NORM_PRIORITY)。

4)我们要知道线程优先级并不是一种直观的机制(我们很难在代码中明显看到) 而通过修改线程优先级所带来的效果通常也不明显。 当提高某个线程的优先级时,可能不会起到任何作用,或者也可能使得某个线程的调度优先级高于其他线程从而导致饥饿。 所以:通常我们尽量不要改变线程的优先级。

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

2.糟糕的响应性
1)如果在GUI程序中使用了后台线程,那么它的响应性可能会很糟糕。 所以我们可以:如果由其他线程完成的工作都是后台任务,那么应该降低它们的优先级,从而提高前台程序的响应性。

2)不良的锁管理也可能导致糟糕的响应性,如果某个线程长时间的占有一个锁,而其他想要访问这个容器的线程就必须等待很长时间。

3.活锁(Livelock)
1)活锁是另一种形式的活跃性问题,该问题尽管不会阻塞线程,但也不能继续执行,因为线程将不断重复执行相同的操作,而且总会失败。

2)活锁通常发生在处理事务消息的应用程序中:如果不能成功地处理某个消息,那么消息处理机制将回滚整个事务,并将它们重新放到队列的开头。 如果消息处理器在处理某种特定类型的消息时存在错误并导致它失败,那么每当这个消息从队列中取出并传递到存在错误的处理器时,都会发生事务回滚。 而由于这条消息又被放到队列开头,因此处理器将被反复调用,并返回相同的结果。 上述情况虽然处理消息的线程并没有阻塞,但也无法执行下去。这种形式的活锁通常是由过度的错误恢复代码造成的,因为它错误地将不可修复的错误作为可修复的错误。

3)当多个相互协作的线程都对彼此进行响应而修改各自的状态,并使得任何一个线程都无法继续执行时,就发生了活锁。
例如:两个过于礼貌的人在半路上面对面的相遇,它们彼此都让出对方的路,然后又在另外的道路上相遇了,就这样彼此让路从而造成活锁。

4)如何解决活锁问题?
要解决活锁问题,需要在重试机制上引入随机性。 在并发应用程序中,通过等待随机长度的时间和回退可以有效地避免活锁的发生。

总结: 活跃性故障是一个非常严重的问题,因为当出现活跃性故障时,除了中止应用程序之外没有其他任何机制可以处理。
最常见的活跃性问题就是锁顺序死锁。 我们在设计应用程序时应该避免产生锁顺序死锁(确保线程在获取多个锁时采用一致的顺序) , 最好的做法是在程序中始终使用开放调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值