死锁的解决方法
产生死锁的四个必要条件:
(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、线程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