- private final static Object MUTEX_READ = new Object();
- private final static Object MUTEX_WRITE = new Object();
- public void read(){
- synchronized (MUTEX_READ) {
- synchronized (MUTEX_WRITE) {
- }
- }
- }
- public void write(){
- synchronized (MUTEX_WRITE) {
- synchronized (MUTEX_READ) {
- }
- }
- }
内存不足
一问一答式的数据交换
服务器开启了某个端口, 等待客户端的访问. 客户端发送请求后等待服务器的响应, 服务器因为某种原因错过了客户端的请求, 让在等待请求. 此时, 服务端可客户端都在等待对方发送数据.
数据库锁
比如某个线程执行了 for update 语句后退出了事务, 其他线程访问的时候就会陷入死锁
文件锁
获取文件锁的线程意外退出, 其它线程无法获取该文件锁会进入死锁.
死循环引起的死锁[系统假死, 查看堆栈信息无法发现任何死锁迹象, CPU 占有率居高不下, 是最为致命以及最难排查的死锁现象]
处理死锁的方法
通过设置某些限制条件, 去破坏产生死锁的四个必要条件中的一个或几个条件, 来防止死锁的发生.[在死锁产生的四个必要条件中,"互斥条件" 是无法破坏的, 破坏 "互斥条件" 会造成结果的不可再现性]
破坏 "不可剥夺条件"
允许对资源实行抢夺.
方法一: 如果占有某些资源的一个线程进行进一步资源请求被拒绝, 则该线程必须释放它最初占有的资源[如果有需要, 可再次请求这些资源和另外的资源] .
方法二: 允许优先级高的线程抢占优先级低的线程的资源.
破坏 "请求和保持条件"
在系统中不允许进程在已获得某种资源的情况下, 申请其他资源.
方法一: 采用 "一次性分配" 方案, 即: 创建进程时, 要求它申请所需的全部资源, 系统或满足其所有要求, 或什么也不给它.
方法二: 要求每个进程提出新的资源申请前, 释放它所占有的资源.
破坏 "循环等待条件"
将系统中的所有资源进行编号, 线程必须按照顺序申请资源.
避免死锁的处理方式
加锁顺序: 给所有的锁排序, 线程只能按照升序 (降序) 的获取锁.[破坏循环等待条件]
加锁超时: 给线程设置获取锁的最大等待时间, 如果超时则放弃对该锁的请求并释放已占有的锁, 等待一段时间后再次请求.[破坏请求和保持条件]
死锁检测
主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景. 每当一个线程请求或者获得了锁, 会在线程和锁相关的数据结构中将其记下.
线程 A 等待线程 B, 线程 B 等待线程 C, 线程 C 等待线程 D, 线程 D 又在等待线程 A. 线程 A 为了检测死锁, 它需要递进地检测所有被 B 请求的锁. 从线程 B 所请求的锁开始, 线程 A 找到了线程 C, 然后又找到了线程 D, 发现线程 D 请求的锁被线程 A 自己持有着. 这是它就知道发生了死锁.
检测和解除死锁
由于操作系统有并发, 共享以及随机性等特点, 通过预防和避免的手段达到排除死锁的目的是很困难的. 一种简便的方法是系统为进程分配资源时, 不采取任何限制性措施, 但是提供了检测和解脱死锁的手段: 能发现死锁并从死锁状态中恢复出来. 因此, 在实际的操作系统中往往采用死锁的检测和解除方法来排除死锁.
死锁检测和解除是指系统设有专门的机制, 当死锁发生时, 该机制能够检测到死锁发生的位置和原因, 并能通过外力破坏死锁发生的必要条件, 从而使得并发进程从死锁状态中恢复出来.
解除死锁
1) 资源剥夺法: 挂起某些死锁进程, 并释放它的资源, 将这些资源分配给其他的死锁进程.[但应防止被挂起的进程长时间得不到资源, 而处于资源匮乏的状态]
2) 撤销线程法: 强制撤销部分, 甚至全部死锁线程.[撤销的原则可以按进程优先级和撤销进程代价的高低进行]
3) 进程回退法: 让一 (多) 个线程回退到足以回避死锁的地步[要求系统保持进程的历史信息, 设置还原点]
来源: https://www.cnblogs.com/BlueStarWei/p/11636566.html