第七章节 - Liveness问题:防止死锁、生命锁和饥饿

Liveness问题:防止死锁、生命锁和饥饿 - 章节


介绍Liveness问题 - 小节


互斥锁和滥用
三个“活性”问题(影响程序的响应能力)
死锁
活锁
饥饿



哲学家和互斥锁
每个哲学家就像一根线
每个叉就像一个互斥体,保护食物
互斥对象可用于受保护的资源
- 例如对象、文件、数据库或硬件
哲学家的例子与通常使用互斥锁的方式略有不同


互斥锁的优缺点
优点:
- 防止竞态条件和其他同步问题
缺点:
- 可能要等
- 带来的性能损失
权衡安全性和性能
- 为什么我们不能让尽可能多的代码同步?


成功设置
哲学家必须能够尽可能多地说话或吃饭
尽量减少尝试获取叉的时间
让哲学家在他们觉得有必要的时候尽快吃东西

 

死锁 - 小节

死锁
线程无法取得进展
- 互斥对象由另一个线程持有
- 没有线程获得它需要的所有互斥
- 没有线程执行它的临界段
- Java死锁涉及两个或多个线程


有向图

 

死锁的结果
没有办法打破Java中的死锁
- 线程被永久阻塞
应用程序可能出现锁定
需要重新启动


恢复
重新启动应用程序并保存日志
用户重试可能会再次锁定系统
修复通常是优先级很高的


示例:
一个非常简单的演示访问两个互斥锁保护的资源
会出什么问题呢?


加等待时间,产生死锁

 

死锁的诊断
期待没有任何进展
- 日志文件可能表明冻结了什么,没有显示任何活动
- GUI可能不再响应/重绘
从一级支持请求jstack
- 几个堆叠几秒钟


示例:
使用jps获取进程id
然后是进程id上的jstack
在文本编辑器中查看输出


其他的死锁
死锁不需要只涉及互斥
一个线程锁定数据库中的表行,请求互斥锁
另一个线程拥有互斥对象,但等待访问锁定的行


死锁策略之一
总是以相同的顺序获取互斥对象
如果互斥对象是按不同的顺序接收的,则应该始终引起怀疑
- 尝试打开窗口,以鼓励出现死锁



死锁策略二
用一个互斥对象替换两个或多个互斥对象
如果在某些地方只使用其中一个互斥锁
- 表现不佳
- 现在不能同时使用

 


死锁策略三
使用try-lock[后退并重试]
- 如果不能立即或在一段时间后获得互斥量
Synchronized不支持这一点
- lock do-参见Java文档


Try-lock范式
如果我们未能获得互斥锁:
- 释放所有的互斥锁
- 再试一次
- 希望在此期间另一个线程能成功
不错的“免牢狱之灾卡”——但最好不是第一个解决方案


哲学家的策略之一
按餐叉的顺序摆放规则
- 已经很棘手了
如果是基于别人在做什么
- 有竞态条件风险,如bar示例中的线程


哲学家策略二
两个令牌充当互斥对象
- 如果可以取令牌,则取两个叉
保证两个哲学家都能吃
完成后,将替换fork和token


哲学家策略三
5秒规则(超时5秒,重试5秒)
- 当然,当处理线程时,周期要小得多!
如果他们都放下叉子并同时重试,问题就会重现
- 能否在重试等待期间进行哲学思考[不陷入僵局]
- 这是活锁

 

活锁 - 小节


活锁
线程被永久阻塞,就像死锁一样。
- 退后,重试
- 等待
- 做一些其他的工作
- 试着解决问题
仍然不能接受所有互斥
- 或者需要很长时间


活锁
“生命锁类似于死锁,只是生命锁所涉及的进程的状态彼此之间不断变化,没有任何进展。”
- 操作系统设计,维基百科

活锁
”这些线程并没有被阻塞——它们只是忙于相互响应而无法恢复工作。“
- 甲骨文,Java教程


回应活锁

 

响应Livelock[非互斥锁问题]
线程之间会阻碍彼此的进程尝试

 

Infinite-Retry活锁
当尝试失败时,不要轻易放弃
- 可恢复错误和不可恢复错误的混淆
例如:电缆拆除后无法恢复的情况
- 应该向用户发送消息,而不是不断重试

 

资源Try-Locking活锁
顾名思义,当使用try-lock时
尝试删除互斥对象以允许另一个线程成功
- 但如果没有休息,我们可能会再捡起来
- 或者如果线程同步超时,它们将获得相同的互斥对象
可以自行清理,但浪费性能


应对生命锁
糟糕的设计——可能需要重新考虑
仲裁可能对走廊案有帮助


处理无限重试生命锁
应该让失败发生(也许在重试之后)
失败案例可能是暂时的故障吗?我们应该失败吗?
禁用程序的[部分]可以吗?
用户是解决这个问题的最佳人选吗?


处理资源Try-Locking Livelock
尝试三种策略
应该先尝试死锁策略吗


策略之一
使用随机值或预定义值更改试锁时间
- 防止线程同步超时的想法
不太可能有效:
- 同步超时是可能的,但不太可能
- 我们可能会立即重新获得所有互斥体


策略二
防止立即再次使用互斥锁
- 如。先做其他工作
- 给其他线程等待成功的时间
如无其他工作可做:
- 睡一小段时间
- 日志的东西


策略三
仲裁如果不能获得互斥
另一个线程[仲裁者]可以建议:
- 让路
- 放弃
- 等待
也可能有助于responding livelock


仲裁在实践中
线程在接受互斥对象之前通知仲裁程序
- 以及他们成功与否
- 失败,仲裁器通知线程做什么
简单的解决方案,所有其他线程,除了一个后退
仲裁可能会很棘手,并导致额外的开销

 

Starvation - 小节


 

饥饿
当线程没有足够的执行时间来执行任务时
- 有时有些人明显比其他人更受欢迎


喜欢饥饿的例子
实时无损数据流
- 如果处理流的早期部分的线程停止工作,流可能会停止
可能是由于访问一个只允许在没有读取器的情况下写入的数据结构造成的
- 如果读者源源不断,作家就会挨饿
- 读者/作者的问题


有利于解决饥饿问题的仲裁
支持处理流的早期部分的线程
请那些在后面的部分工作的人后退-仲裁
回退线程休眠或提供更少的工作


解决读者/作者的问题
可以试着偏爱作家吗
- 但要小心,作家不会让读者挨饿
或者暂时屏蔽读者,让作者成功


由于不公平的日程安排而造成的饥饿
在我们的控制之外——我们希望JVM调度程序是公平的
如果我们改变线程的优先级呢?
- 高优先级线程导致低优先级线程饥饿的危险
- 系统的依赖
- 做这件事要非常小心


工作窃取
如果线掉线了
另一个线程可以窃取它的一些工作
然后它会丢弃任何偷来的物品


由于缺乏资源而挨饿
线程资源不足
原因:
- 许多线程
- 系统资源太少
- (如攻击。例如:拒绝服务)
可能影响所有线程


解决资源饥饿
减少线程数
- Amdahl定律和实验
监控系统资源,寻找不足之处
- 如果可能的话可能会增加

 

活性问题总结 - 小节

 

活性来自互斥体
死锁
活锁
饥饿


死锁
一个线程在等待互斥对象,另一个线程在等待
- 一个线程正在等待一个互斥对象,另一个线程…
- 没有线程可以前进
通过注意应用程序没有进展进行诊断
- 检查日志/ jstack


解决死锁
策略
- 以相同的顺序获取互斥对象
- 减少互斥量
- 用try-lock


活锁
线程无法获得所需的资源,但不会被永久阻塞
- 线程无法继续,但可以后退并重试


三种活锁
回应活锁
- 反思设计,也许仲裁
Infinite-retry活锁
- 应该不能操作
资源try-lock活锁
- 死锁策略,更改超时,重试前等待,仲裁


饥饿
某些或所有线程无法执行工作
- 执行时间不够
原因:
- 不公平调度器
- 改变线程优先级
- 足够的资源
- 支持线程


解决饥饿
支持线程(读者/作者):
- 阻止其他线程成功
仲裁
工作窃取
资源饥饿:
- 调整线程更好
- 整理资源

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值