线程死锁的问题
2016-07-13 23:32
141 查看
何谓死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待放弃自己需要的同步资源。
形象化的说;例如两个人A和B吃饭,本来A和B每人一双筷子,可以愉快的吃饭,
现在是A拿一根筷子,B拿一根,谁也不肯放弃,就在这僵持着,导致死锁。
代码如下:
两个线程:
当第一个线程启动时,会握住sb1的锁,当此时线程进入休眠状态,让出cpu的执行权,由第二个线程获取到了cpu执行权,二线程握住了sb2锁,
此时二线程休眠,一线程又阻塞变为运行,但是由于锁sb2已被二线程占用,一线程只能等待二线程释放sb2锁,但是二线程由由于一线程占用sb1锁,
导致二线程也等待一线程适当sb1的锁,进入等待状态,从而程序进入死锁,无法继续运行。
解决死锁的方式:
使用特殊算法,减少同步资源的定义
1.安全序列
我们首先引入安全序列的定义:所谓系统是安全的,是指系统中的所有进程能够按照某一种次序分配资源,并且依次地运行完毕,这种进程序列{P1,P2,...,Pn}就是安全序列。如果存在这样一个安全序列,则系统是安全的;如果系统不存在这样一个安全序列,则系统是不安全的。
安全序列{P1,P2,...,Pn}是这样组成的:若对于每一个进程Pi,它需要的附加资源可以被系统中当前可用资源加上所有进程Pj当前占有资源之和所满足,则{P1,P2,...,Pn}为一个安全序列,这时系统处于安全状态,不会进入死锁状态。
虽然存在安全序列时一定不会有死锁发生,但是系统进入不安全状态(四个死锁的必要条件同时发生)也未必会产生死锁。当然,产生死锁后,系统一定处于不安全状态。
2.银行家算法
这是一个著名的避免死锁的算法,是由Dijstra首先提出来并加以解决的。
[背景知识]
一个银行家如何将一定数目的资金安全地借给若干个客户,使这些客户既能借到钱完成要干的事,同时银行家又能收回全部资金而不至于破产,这就是银行家问题。这个问题同操作系统中资源分配问题十分相似:银行家就像一个操作系统,客户就像运行的进程,银行
b64d
家的资金就是系统的资源。
[问题的描述]
一个银行家拥有一定数量的资金,有若干个客户要贷款。每个客户须在一开始就声明他所需贷款的总额。若该客户贷款总额不超过银行家的资金总数,银行家可以接收客户的要求。客户贷款是以每次一个资金单位(如1万RMB等)的方式进行的,客户在借满所需的全部单位款额之前可能会等待,但银行家须保证这种等待是有限的,可完成的。
例如:有三个客户C1,C2,C3,向银行家借款,该银行家的资金总额为10个资金单位,其中C1客户要借9各资金单位,C2客户要借3个资金单位,C3客户要借8个资金单位,总计20个资金单位。某一时刻的状态如图所示。
银行家算法示意
对于a图的状态,按照安全序列的要求,我们选的第一个客户应满足该客户所需的贷款小于等于银行家当前所剩余的钱款,可以看出只有C2客户能被满足:C2客户需1个资金单位,小银行家手中的2个资金单位,于是银行家把1个资金单位借给C2客户,使之完成工作并归还所借的3个资金单位的钱,进入b图。同理,银行家把4个资金单位借给C3客户,使其完成工作,在c图中,只剩一个客户C1,它需7个资金单位,这时银行家有8个资金单位,所以C1也能顺利借到钱并完成工作。最后(见图d)银行家收回全部10个资金单位,保证不赔本。那麽客户序列{C1,C2,C3}就是个安全序列,按照这个序列贷款,银行家才是安全的。否则的话,若在图b状态时,银行家把手中的4个资金单位借给了C1,则出现不安全状态:这时C1,C3均不能完成工作,而银行家手中又没有钱了,系统陷入僵持局面,银行家也不能收回投资。
综上所述,银行家算法是从当前状态出发,逐个按安全序列检查各客户谁能完成其工作,然后假定其完成工作且归还全部贷款,再进而检查下一个能完成工作的客户,......。如果所有客户都能完成工作,则找到一个安全序列,银行家才是安全的。
从上面分析看出,银行家算法允许死锁必要条件中的互斥条件,占有且申请条件,不可抢占条件的存在,这样,它与预防死锁的几种方法相比较,限制条件少了,资源利用程度提高了。
这是该算法的优点。其缺点是:
〈1〉这个算法要求客户数保持固定不变,这在多道程序系统中是难以做到的。
〈2〉这个算法保证所有客户在有限的时间内得到满足,但实时客户要求快速响应,所以要考虑这个因素。
〈3〉由于要寻找一个安全序列,实际上增加了系统的开销。
package com.atgulgu.java;
//死锁的问题,处理线程同步时容易出现
public class TestDeadLock {
static StringBuffer sb1 = new StringBuffer();
static StringBuffer sb2 = new StringBuffer();
public static void main(String[] args) {
new Thread(){
public void run(){
synchronized(sb1){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
sb1.append("A");
synchronized(sb2){
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread(){
public void run(){
synchronized(sb2){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
sb1.append("C");
synchronized(sb1){
sb2.append("D");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}
不同的线程分别占用对方需要的同步资源不放弃,都在等待放弃自己需要的同步资源。
形象化的说;例如两个人A和B吃饭,本来A和B每人一双筷子,可以愉快的吃饭,
现在是A拿一根筷子,B拿一根,谁也不肯放弃,就在这僵持着,导致死锁。
代码如下:
两个线程:
当第一个线程启动时,会握住sb1的锁,当此时线程进入休眠状态,让出cpu的执行权,由第二个线程获取到了cpu执行权,二线程握住了sb2锁,
此时二线程休眠,一线程又阻塞变为运行,但是由于锁sb2已被二线程占用,一线程只能等待二线程释放sb2锁,但是二线程由由于一线程占用sb1锁,
导致二线程也等待一线程适当sb1的锁,进入等待状态,从而程序进入死锁,无法继续运行。
解决死锁的方式:
使用特殊算法,减少同步资源的定义
1.安全序列
我们首先引入安全序列的定义:所谓系统是安全的,是指系统中的所有进程能够按照某一种次序分配资源,并且依次地运行完毕,这种进程序列{P1,P2,...,Pn}就是安全序列。如果存在这样一个安全序列,则系统是安全的;如果系统不存在这样一个安全序列,则系统是不安全的。
安全序列{P1,P2,...,Pn}是这样组成的:若对于每一个进程Pi,它需要的附加资源可以被系统中当前可用资源加上所有进程Pj当前占有资源之和所满足,则{P1,P2,...,Pn}为一个安全序列,这时系统处于安全状态,不会进入死锁状态。
虽然存在安全序列时一定不会有死锁发生,但是系统进入不安全状态(四个死锁的必要条件同时发生)也未必会产生死锁。当然,产生死锁后,系统一定处于不安全状态。
2.银行家算法
这是一个著名的避免死锁的算法,是由Dijstra首先提出来并加以解决的。
[背景知识]
一个银行家如何将一定数目的资金安全地借给若干个客户,使这些客户既能借到钱完成要干的事,同时银行家又能收回全部资金而不至于破产,这就是银行家问题。这个问题同操作系统中资源分配问题十分相似:银行家就像一个操作系统,客户就像运行的进程,银行
b64d
家的资金就是系统的资源。
[问题的描述]
一个银行家拥有一定数量的资金,有若干个客户要贷款。每个客户须在一开始就声明他所需贷款的总额。若该客户贷款总额不超过银行家的资金总数,银行家可以接收客户的要求。客户贷款是以每次一个资金单位(如1万RMB等)的方式进行的,客户在借满所需的全部单位款额之前可能会等待,但银行家须保证这种等待是有限的,可完成的。
例如:有三个客户C1,C2,C3,向银行家借款,该银行家的资金总额为10个资金单位,其中C1客户要借9各资金单位,C2客户要借3个资金单位,C3客户要借8个资金单位,总计20个资金单位。某一时刻的状态如图所示。
|
|
|
|
(a) | (b) | (c) | (d) |
银行家算法示意
对于a图的状态,按照安全序列的要求,我们选的第一个客户应满足该客户所需的贷款小于等于银行家当前所剩余的钱款,可以看出只有C2客户能被满足:C2客户需1个资金单位,小银行家手中的2个资金单位,于是银行家把1个资金单位借给C2客户,使之完成工作并归还所借的3个资金单位的钱,进入b图。同理,银行家把4个资金单位借给C3客户,使其完成工作,在c图中,只剩一个客户C1,它需7个资金单位,这时银行家有8个资金单位,所以C1也能顺利借到钱并完成工作。最后(见图d)银行家收回全部10个资金单位,保证不赔本。那麽客户序列{C1,C2,C3}就是个安全序列,按照这个序列贷款,银行家才是安全的。否则的话,若在图b状态时,银行家把手中的4个资金单位借给了C1,则出现不安全状态:这时C1,C3均不能完成工作,而银行家手中又没有钱了,系统陷入僵持局面,银行家也不能收回投资。
综上所述,银行家算法是从当前状态出发,逐个按安全序列检查各客户谁能完成其工作,然后假定其完成工作且归还全部贷款,再进而检查下一个能完成工作的客户,......。如果所有客户都能完成工作,则找到一个安全序列,银行家才是安全的。
从上面分析看出,银行家算法允许死锁必要条件中的互斥条件,占有且申请条件,不可抢占条件的存在,这样,它与预防死锁的几种方法相比较,限制条件少了,资源利用程度提高了。
这是该算法的优点。其缺点是:
〈1〉这个算法要求客户数保持固定不变,这在多道程序系统中是难以做到的。
〈2〉这个算法保证所有客户在有限的时间内得到满足,但实时客户要求快速响应,所以要考虑这个因素。
〈3〉由于要寻找一个安全序列,实际上增加了系统的开销。
package com.atgulgu.java;
//死锁的问题,处理线程同步时容易出现
public class TestDeadLock {
static StringBuffer sb1 = new StringBuffer();
static StringBuffer sb2 = new StringBuffer();
public static void main(String[] args) {
new Thread(){
public void run(){
synchronized(sb1){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
sb1.append("A");
synchronized(sb2){
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread(){
public void run(){
synchronized(sb2){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
sb1.append("C");
synchronized(sb1){
sb2.append("D");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}
相关文章推荐
- 剑指Offer--063-二叉搜索树的第K个结点
- Java并发编程总结2——慎用CAS(转)
- unity模型法线反转问题
- 监控Linux性能的18个命令行工具
- unity3d第三天02(数组)
- Easyui 一些应用
- SSO-C#跨域单点登录(一)
- 想做Android Wear开发?你得先搞明白这四件事
- PhotoShop教程:制作玻璃水晶质感文字
- Linux中的可用内存指的是什么?
- ESP8266 AT指令开发篇(一)
- 巧用ASP.NET中的Web服务器控件
- error LNK2001: unresolved external symbol _IID_ISampleGrabberCB
- mysql优化--博森瑞
- hibernate之关联映射
- MySQL索引原理及慢查询优化
- webpack + vue.js + vue route
- 黑马程序员_Java基础_我的Day26学习笔记
- 瀑布流UICollectionViewFlowLayout/UICollectionViewLayout
- 絮语----工作四年的碎碎念