黑马程序员技术交流社区
标题:
(主推) 死锁总结
[打印本页]
作者:
朱晓杰
时间:
2013-5-7 21:33
标题:
(主推) 死锁总结
本帖最后由 朱晓杰 于 2013-5-7 23:26 编辑
一、理解死锁
经典的"哲学家进餐"的问题很好的描述了死锁状况。
5个哲学家去吃中餐,坐在一张圆桌旁。他们有5根筷子(而不是5双),并且每两个人之间放一根筷子,
哲学家们时而思考,时而进餐。每个人都需一双筷子才能吃到东西,并在吃完后将筷子放到原处继续思考。
如果每个人都立即抓住自己左边的筷子,然后等待右边的筷子空出来,但同时又不放下已经拿到的筷子。
这种情况将产生死锁:每个人都拥有其他人需要的资源,同时又等待其他人已经拥有的资源,并且每个人
在获得所有需要的资源之前都不会放弃已经拥有的资源。
死锁(DeadLock),是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。
产生死锁的四个必要条件是:
1:互斥条件:资源每次只能被一个线程使用。如前面的“线程同步代码段”。
2:请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
3:不剥夺条件:进程已获得的资源,在未使用完之前,无法强行剥夺。
4:循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
二、死锁示例
1.最简单的死锁形式
在线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L那么这两个线程将永远的等待下去
public class DeadLockDemo {
public static void main(String[] args) {
DeadLock d = new DeadLock();
Thread a = new Thread(d);
Thread b = new Thread(d);
a.start();
b.start();
}
}
class DeadLock implements Runnable{
private boolean flag = true;//控制线程的操作交替进行
private final Object l = new Object();
private final Object m = new Object();//定义两个静态锁
public void run(){
while(true){
if(flag){
synchronized(l){//①
System.out.println("...if l...");
synchronized(m){//②
System.out.println("...if m...");
}
}
flag = false;
}else{
synchronized(m){//③
System.out.println("...else m...");
synchronized(l){//④
System.out.println("...else l...");
}
}
flag = true;
}
}
}
}
复制代码
发生死锁的原因:两个线程试图以不同的顺序来获得相同的锁。
分析:
死锁分析.png
(10.15 KB, 下载次数: 2)
下载附件
死锁分析
2013-5-7 21:30 上传
线程a执行到①,锁定了M,此时线程b抢占了CPU,获得了执行权,锁定了L,而线程a对M的锁定还没有解除,导致线程b无法运行下去,同样由于线程b锁定了L,线程a也无法运行。
2.银行转账死锁示例
/*
* 需求:简单的银行转账,它将资金从一个账户转到另一个账户
* 在开始转账之前,需要获得两个Account的锁,以确保以原子的方式更新账户中的余额,且不能破坏不可变的条件,如账户的余额不能为负数
*/
/*账户类*/
class Account{
private String accountName;//账号
private int balance;//资金总额
public Account(String accountName,int balance){
this.accountName = accountName;
this.balance = balance;
}
/**/
public String getAccountName() {//获取账号
return accountName;
}
public int getBalance() {//获取账号余额
return balance;
}
public void debit(int amount){//更新转出方余额
this.balance -= amount;
}
public void credbit(int amount){//更新转入方余额
this.balance += amount;
}
}
class TransferAccount implements Runnable{
public Account fromAccount;//转出账户
public Account toAccount;//转入账户
public int amount;//转出金额
public TransferAccount(Account fromAccount,Account toAccount,int amount){
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
public void run(){
while(true){
synchronized(fromAccount){
synchronized(toAccount){
if(fromAccount.getBalance() <= 0){//转账进行的条件:判断转出账户的余额是否大于0
System.out.println(fromAccount.getAccountName() + "账户余额不足,无法进行转账");
return;
}else{
fromAccount.debit(amount);//更新转出账户的余额:-
toAccount.credbit(amount);//更新转入账户的余额:+
}
}
}
System.out.println(fromAccount.getAccountName() + "......" + fromAccount.getBalance());//打印转出账户的余额
System.out.println(toAccount.getAccountName() + "---" + toAccount.getBalance());//打印转入账户的余额
}
}
}
public class BankTransfer {
public static void main(String[] args) {
Account fromAccount = new Account("张三",100000);
Account toAccount = new Account("李四",200000);
Thread a = new Thread(new TransferAccount(fromAccount,toAccount,1));
Thread b = new Thread(new TransferAccount(toAccount,fromAccount,2));
a.start();
b.start();
}
}
复制代码
发生死锁的原因:两个线程试图以不同的顺序来获得相同的锁
分析:所有的线程并不是按照相同的顺序来获取锁,锁的顺序取决于传递的参数顺序,而这些参数顺序又取决于外部输入,如果线程a和线程b同时转账,线程a从张三向李四转账,线程b从李四向张三转账,那么就会发生死锁:
a:new TransferAccount(fromAccount,toAccount,1)
b:new TransferAccount(toAccount,fromAccount,2)
如果执行顺序不恰当,那么a可能获得fromAccount的锁并且等待toAccount的锁,然而此时b拥有toAccount的锁,并正在等待fromAccount的锁。
作者:
朱晓杰
时间:
2013-5-7 23:28
抢楼!今天困了,明天如果还有时间,再来一个Demo!
作者:
朱晓杰
时间:
2013-5-8 09:15
谢版主赐婚!
作者:
张林敏
时间:
2013-5-8 12:43
不错,顶一个...
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2