死锁

为了线程安全,我们在需要的是会使用”独占锁“,但过多的锁定也会有麻烦。多个线程因为竞争资源相互等待而造成的僵局,我们称为死锁。若无外力作用,这些进程将都无法推进。在死锁中,至少有两个线程被挂起,并等待对方解除锁定。

我们先看一个小例子:在一个平面上有很多方块,有一些星星在这上面移动。基本规则就是:每个方块只能有一个星星。

(图1)

移动过程如下图,小黑想从a2方块移动到a3方块。这时小黑得占用a2位置,当a3位置为空时跳过去。

(图2)

当想移动的目标位置被其他星星占着的时候,就等其他星星移走后再移动过去。如下图小绿,会等到小黑移走后再移动到a2。

(图3)

但当小黑想移动的目标位置是b2的时候就悲剧了,固执的星星小黑和小绿将相互等待对方移走,直到永远。

(图4)

可能会出现更复杂的情况,小黑在等待小红移走,小红在等待小蓝移走,小蓝在等待小绿移走,小绿在等待小黑移走。这样僵持着!

(图5)

或更更复杂的情况(脑补)。。。

产生死锁的条件

产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。

  • 互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。对应我们例子就是,每个方块只能容的下一个星星。
  • 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。对应我们的例子就是,星星只能等待目标位置星星自己走,而不能把它推下方块。
  • 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。对应我们的例子就是,星星得占着一个方块,然后找目标方块。
  • 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。

预防死锁

从我们的例子出发,我们能想到的方法有:
1. 让星星只能按顺序跳,例如规定的顺序是a0...an...b1...bn...c1...cn。当然,有能力的星星可以跨越着跳,例如从a0到c1,但不能违背顺序逆着来。当到想回头的时候,就必须先下了方块(释放锁)再从头开始。
2. 当星星想移动的目标位置被其他星星占着的时候,就等其他星星移走后再移动过去。这是星星的移动规则,我们可以在这里加个时限,当等待的时间超过时限,就运行星星“休息”,星星就先下方块(释放锁),休息一段时间后再继续。
3. 如果星星的移动线路是比较简单的,例如每次启动只移动5步,则可以采用预定的方法。星星开始移动的时候就锁定所有将移动的方块。
4. 改变星星的基本移动方法,例如从a0->a1,移动过程改成,先下方块a0(释放资源),然后再到a1上。
5. 在4的基础上改进,每次都要下方块比较不方便,所以我们改成,当需要等待移动目标方块上星星移走的时候再先下方块。 
6. 找个管理者,当发现锁死的时候,把这些固执的星星都叫下方块。让每个星星随机等一段时间再继续。
第1种方案:按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁,但总有些时候是无法预知的。基本思想就是破坏产生死锁的必要条件——循环等待条件。
第2种方案:是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该线程在没有获得锁的时候可以继续运行(加锁超时后可以先继续运行干点其它事情,再回头来重复之前加锁的逻辑)。这算是一种预防,没破坏死锁的必要条件。且,如果多个线程同时请求几个资源的时候,因为等待的时间有一样,容易出现重复尝试,始终得不到锁。

第3种方案:这种方案比较暴力,杜绝了死锁。但如果一个线程需要很多资源的时候,很浪费。且容易出现,因为所需的某一两种资源不能满足而不给分配资源。

第4、5种方案:这种方案破坏产生死锁的必要条件——请求和保持条件。但并不是什么情况下都能这么用。

第6种方案:这个就是死锁检测,当发现死锁的时候进行处理。

一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁。

一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。

C# 多线程系列(五)的更多相关文章

  1. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  2. java多线程系列五、并发容器

    一.ConcurrentHashMap 1.为什么要使用ConcurrentHashMap 在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,HashMap在 ...

  3. Java多线程系列五——列表类

    参考资料: http://xxgblog.com/2016/04/02/traverse-list-thread-safe/ 一些列表类及其特性  类 线程安全 Iterator 特性 说明 Vect ...

  4. 【Java多线程系列五】列表类

    一些列表类及其特性  类 线程安全 Iterator 特性 说明 Vector 是 fail-fast 内部方法用synchronized修饰,因此执行效率较低 1. 线程安全的列表类并不意味着调用它 ...

  5. (Java多线程系列五)守护线程

    守护线程 什么是守护线程 Java中有两种线程,一种是用户线程,一种是守护线程. 当进程不存在或主线程停止,守护线程也会自动停止. class DaemonThread extends Thread ...

  6. Java多线程系列目录(共43篇)

    最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...

  7. java多线程系列(一)

    java多线程技能 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...

  8. java多线程系列(二)

    对象变量的并发访问 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...

  9. java多线程系列(三)---等待通知机制

    等待通知机制 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解 ...

  10. java多线程系列(四)---Lock的使用

    Lock的使用 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理 ...

随机推荐

  1. Docker学习总结(18)——阿里超大规模Docker化之路

    12月6-7日,由阿里巴巴集团.阿里巴巴技术发展部.阿里云云栖社区联合主办,以"2016双11技术创新"为主题的阿里巴巴技术论坛上,阿里巴巴研究员林昊分享了阿里超大规模Docker ...

  2. mySQL and sqoop for ubuntu

    数据的导入导出 ——MySQL & sqoop in Ubuntu 1.完成搭建hadoop集群 2.安装MySQL sudo apt-get install mysql-server mys ...

  3. kendo grid 点击更新没有反映

    因为没有在dataSource上写schema schema: { model: { id: "DeptId", fields: { CompanyId: { editable: ...

  4. Ubuntu 16.04粘贴板增强工具Parcellite

    默认在Ubuntu已经有粘贴板增强工具,比如保存最近五个的复制文本历史. 1.系统默认 使用:[Ctrl]+[;] 然后用数字进行选择即可,但是只支持文本,只能保存最近5次 2.安装Parcellit ...

  5. sizeThatFits and sizeToFit

    http://liuxing8807.blog.163.com/blog/static/9703530520134381526554/ sizeThatFits and sizeToFit是UIVie ...

  6. 一个经典的消费者和生产者的实现(linux )

    #include <stdio.h>   #include <pthread.h>   #define BUFFER_SIZE 16 // 缓冲区数量       struct ...

  7. 最全Pycharm教程(37)——Pycharm版本号控制之基础篇

    1.主题 介绍Pycharm的版本号控制系统 2.准备工作 (1)Pycharm版本号为2.7或者更高 (2)已经创建一个project.參见Getting Started tutorial (3)安 ...

  8. 【ora10,4】oracle后台进程介绍:

    一.SMON(System MONitor) 系统监控进程:      在数据库启动过程中,SMON排在CKPT进程之后,在Oracle9i中排在第六号的位置: PMON started with p ...

  9. MVC之使用Nhibernate

    NHibernate是一个基于.Net,用于关系数据库的对象持久化类库.它是著名的Hibernate的.Net版本,NHibernate用于把你的.Net对象持久化到底层的关系数据库中.你完全不用自己 ...

  10. 一个JS多个数组取交集算法

    如题,多个数组中取交集(共同拥有元素),思路取第一个数组去跟每个数组中的元素对比,同时比较数据类型有救返回没有就返回null. 下面介绍到的算法数据格式是二维数组如: const parentArra ...