死锁的形成及解决方法(如何排查死锁)

39 篇文章 0 订阅
23 篇文章 0 订阅

死锁的解决方法

产生死锁的四个必要条件:
(1) 互斥条件:资源不能共享,一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

1.安全序列
我们首先引入安全序列的定义:所谓系统是安全的,是指系统中的所有进程能够按照某一种次序分配资源,并且依次地运行完毕,这种进程序列{P1,P2,…,Pn}就是安全序列。如果存在这样一个安全序列,则系统是安全的;如果系统不存在这样一个安全序列,则系统是不安全的。
安全序列{P1,P2,…,Pn}是这样组成的:若对于每一个进程Pi,它需要的附加资源可以被系统中当前可用资源加上所有进程Pj当前占有资源之和所满足,则{P1,P2,…,Pn}为一个安全序列,这时系统处于安全状态,不会进入死锁状态。  
虽然存在安全序列时一定不会有死锁发生,但是系统进入不安全状态(四个死锁的必要条件同时发生)也未必会产生死锁。当然,产生死锁后,系统一定处于不安全状态。
2.银行家算法
这是一个著名的避免死锁的算法,是由Dijstra首先提出来并加以解决的。 
[背景知识]
一个银行家如何将一定数目的资金安全地借给若干个客户,使这些客户既能借到钱完成要干的事,同时银行家又能收回全部资金而不至于破产,这就是银行家问题。这个问题同操作系统中资源分配问题十分相似:银行家就像一个操作系统,客户就像运行的进程,银行家的资金就是系统的资源。

参考:
https://blog.csdn.net/caozhao3344/article/details/77199552

MySQL中的死锁及排查

MySQL中也会产生死锁,同样是竞争资源相互等待。
比如:
先查出8,9两行数据,准备进行更新,这时会将8,9两行上锁
第一种情况:

例如(以下会话id为主键):
 
Session1:
 
mysql> select * from t3 where id in (8,9) for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
|  8 | WA     | f    | 2016-03-02 11:36:30 |
|  9 | JX     | f    | 2016-03-01 11:36:30 |
+----+--------+------+---------------------+
rows in set (0.04 sec)
 
 
Session2:
select * from t3 where id in (10,8,5) for update;
锁等待中……
 
其实这个时候id=10这条记录没有被锁住的,但id=5的记录已经被锁住了,锁的等待在id=8的这里
不信请看
 
Session3:
mysql> select * from t3 where id=5 for update;
锁等待中
 
 
Session4:
mysql> select * from t3 where id=10 for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
| 10 | JB     | g    | 2016-03-10 11:45:05 |
+----+--------+------+---------------------+
row in set (0.00 sec)
 
 
在其它session中id=5是加不了锁的,但是id=10是可以加上锁的。

第二种情况

以id为主键为例,目前还没有id=22的行
 
Session1:
select * from t3 where id=22 for update;
Empty set (0.00 sec)
 
session2:
select * from t3 where id=23  for update;
Empty set (0.00 sec)
 
Session1:
insert into t3 values(22,'ac','a',now());
锁等待中……
 
Session2:
insert into t3 values(23,'bc','b',now());
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

更多情况,见:MySQL死锁

知道死锁之后,如何进行排查呢?咱也不会啥技巧,就会硬刚。

  1. 多打日志看
  2. 根据死锁成因分析原因
  3. 破坏其中一环即可

死锁解决实例:

先来看死锁:就是多个锁和多个线程之间发生的故事,这里以俩个锁和俩个线程为例子。

锁1、锁2、线程1、线程2

当线程1获取锁1之后,线程2也获取了锁2,这是线程1又开始获取锁2(嵌套获取),线程2又想获取锁1(嵌套1获取),这种情况下线程1会等着线程2释放锁2,线程2会等着线程1释放锁1,然后就会形成了死锁。

具体代码:

public class deadLock implements Runnable{
 
	//1.创建俩把锁
	public static final Object object1=new Object();
	public static final Object object2=new Object();
	boolean boo;
	@Override
	public void run() {
		if(boo) {
			synchronized (object1) {
				System.out.println("进入1锁");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (object2) {
					System.out.println("进入2锁");
				}
			}
		}else {
			synchronized(object2) {
				System.out.println("进入2锁");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (object1) {
					System.out.println("进入1锁");
				}
			}
		}
		
	}
	
	
}

测试代码:

public static void main(String[] args) {
		deadLock d1=new deadLock();
		deadLock d2=new deadLock();
		d1.boo=true;
		d2.boo=false;
		Thread t1=new Thread(d1);
		Thread t2=new Thread(d2);
		t1.start();
		t2.start();
	}

怎么解决呢?
这种解决方式可以有三种,
1.顺序取锁
2.设置锁释放时间(可以使用Lock来实现)
3.预防死锁
接下来贴出第一个方式的代码:

对比死锁的代码来看,其实就是让所有的线程获取所得顺序是一样的,这样子的话就不会存在什么线程死锁的情况出现。

public class openLock implements Runnable{
	
	public static final Object object=new Object();
	public static final Object object1=new Object();
	boolean bool;
	@Override
	public void run() {
		if(bool) {
			synchronized (object) {
				System.out.println("进入1锁");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (object1) {
					System.out.println("进入二锁");
				}
			}
		}else {
			synchronized (object) {
				System.out.println("进入1锁");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (object1) {
					System.out.println("进入2锁");
				}
			}
		}
		
	}
	
	
}

第二种方法:

这种方法就是利用Lock来实现的,Lock锁有一种说法叫做可中断锁,意思是当等待的线程达到一定的时间之后,就不会再去等待了,它会放弃运行这个锁方法,而去执行其他方法。

public class OpenLock implements Runnable{
//第二种解决死锁的方式是:给锁设置一个时间,如果超过这个时间的或就放弃获取这个锁。
	Lock l1=new ReentrantLock();//锁1
	Lock l2=new ReentrantLock(); //锁2
	boolean bool;
	@Override
	public void run() {
		if(bool) {
			try {
			l1.lock();
			System.out.println("加上锁1");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("现在我在等待进入锁二了");
			if(l2.tryLock(200, TimeUnit.MILLISECONDS)){//注意在判断这一步就已经加上锁了
                try {
                    System.out.println("进入锁二");
                }finally {
                    l2.unlock();
                }
            }else{
                System.out.println("锁二一直被占用,算了我不进去了");
            }
			
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally {
				l1.unlock();
			}
		}else {
			l2.lock();
			try {
				System.out.println("进入锁二");
				Thread.sleep(1000);
				System.out.println("现在我在等待进入锁1了");
				if(l1.tryLock(200,TimeUnit.MILLISECONDS)) {
					
					try {
						System.out.println("进入锁一");
					}finally {
						l1.unlock();
					}
				}else {
					System.out.println("等带时间太长了,我不等了。");
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally {
				
			}
		}
		
	}
	
}

参考:
https://blog.csdn.net/qq_38638148/article/details/82725191

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值