1. 非死锁缺陷
非死锁缺陷占了并发问题的大多数(97%)
1.1 违反原子性缺陷
含义:代码段本意是原子的,但是子执行过程并没有强制实现原子性
解决办法:加锁
1.2 违反顺序缺陷
含义:A应该在B之前执行,但是实际上却不是这个顺序
解决办法:条件变量(实际上还要用到锁和状态的变量)
2. 死锁缺陷
含义:当线程1持有锁L1,正在等待另外一个锁L2,线程2持有锁L2,却在等待锁L1释放,死锁产生。
为什么死锁容易发生?
- 大型代码库中,组件之间有复杂的依赖
- 封装(不知道细节导致的)
死锁产生的条件:
- 互斥:线程对需要的资源进行互斥访问
- 持有并等待:线程持有了资源,又在等待其他资源
- 非抢占:如锁不能被抢占
- 循环等待:线程之间存在环路
解决办法如下:
2.1 针对循环等待
获得锁的时候提供全序或者偏序
技巧:通过锁的地址来强制锁的顺序
2.2 针对持有并等待
原子地强锁,相当于增加一个全局锁来控制锁的请求
这种方式不适用封装,而且降低并发度
2.3 针对非抢占
可以使用trylock(),尝试获得锁,如果锁被占用,则释放持有资源,等待一段时间
问题1:可能出现活锁(两个线程一直重复该方式,又同时强锁失败);解决办法:随机等待一个时间
问题2:封装导致恢复到未持有锁之前的状态比较困难
2.4 针对互斥
无锁编程,利用硬件指令,比如原子的比较并交换指令(比较变量是否等于期待值,如果是则赋给它新值,并返回1,如果不等于返回0,什么都不做)
困难:实现比较困难
2.5 通过调度避免死锁
基本原理:让可能出现死锁是线程顺序执行
缺陷:影响性能
2.6 恢复并检查
死锁检测器定期检查(通过构建资源图),检查到死锁,系统重启