初识死锁问题

简介: 关于对死锁的详解

gs) {

//创建两把锁

Object loker1=new Object();

Object loker2=new Object();

//创建两个不同的线程

Thread t1=new Thread(()->{

System.out.println(Thread.currentThread().getName()+"试图获取到锁1");

synchronized(loker1){

System.out.println(Thread.currentThread().getName()+"获取到锁1");

try {

Thread.sleep(3000);

System.out.println(Thread.currentThread().getName()+"试图获取到锁2");

synchronized (loker2){

System.out.println(Thread.currentThread().getName()+"获取到锁2");

}

} catch (InterruptedException e) {

throw new RuntimeException(e);

}


}

},"t1");

Thread t2=new Thread(()->{

HashMap<Integer, Integer> integerIntegerHashMap = new HashMap<>();

System.out.println(Thread.currentThread().getName()+"试图获取到锁2");

synchronized(loker2){

System.out.println(Thread.currentThread().getName()+"获取到锁2");

try {

Thread.sleep(3000);

System.out.println(Thread.currentThread().getName()+"试图获取到锁1");

synchronized (loker1){

System.out.println(Thread.currentThread().getName()+"获取到锁1");

}

} catch (InterruptedException e) {

throw new RuntimeException(e);

}


}

},"t2");

t1.start();

t2.start();


}

}

其结果如下:

image.png

两个线程两把锁是多线程多把锁的特殊案例,我们下面将对多个线程多把锁的场景进行介绍:哲学家吃面问题:

image.png

五个哲学家吃一碗面,每个人左右两个 各有一双筷子,吃面规则是:每个哲学家先拿自己左手边的筷子,再拿自己右手边的筷子,吃一口之后把筷子放下,让其他哲学家吃面,对于一般情况而言,每个哲学家吃完一口,就把筷子放下了,不影响其他哲学家吃面,但是出现了下面这种情况:①号-⑤号哲学家依次拿起了自己左手边的筷子,然后想去拿自己右手的筷子,却发现右手边的筷子被右手边的科学家拿着不放,从而造成死锁问题。

我们去分析问题产生的原因:

①互斥访问:线程1拿到了锁A,线程2就不能再获取该锁

②不可抢占:已经获取锁资源的线程,除非自己手动释放锁,其他线程不能从这个线程的手中夺取锁资源

③保持与请求:线程1已经获取了锁A,这时候还想要再获取锁B

④循环等待:线程1等待线程2释放锁,线程2等待线程3释放锁,线程3等待线程1释放锁......

那么我们应该如何解决死锁问题呢?

我们依然从这四个方面进行分析

①互斥访问:锁的基本特性,不能打破

②不可抢占:锁的基本特性,不能打破

③保持与请求:从代码实现和设计的角度我们可以改变保持与请求的顺序,也就是获取锁的顺序来改变死锁问题

我们改变一下上述代码获取锁的顺序:

import java.util.HashMap;


/**

* @author tongchen

* @create 2023-02-09 17:34

*/

public class DeadLocker {

public static void main(String[] args) {

//创建两把锁

Object loker1=new Object();

Object loker2=new Object();

//创建两个不同的线程

Thread t1=new Thread(()->{

System.out.println(Thread.currentThread().getName()+"试图获取到锁1");

synchronized(loker1){

System.out.println(Thread.currentThread().getName()+"获取到锁1");

try {

Thread.sleep(3000);

System.out.println(Thread.currentThread().getName()+"试图获取到锁2");

synchronized (loker2){

System.out.println(Thread.currentThread().getName()+"获取到锁2");

}

} catch (InterruptedException e) {

throw new RuntimeException(e);

}


}

},"t1");

Thread t2=new Thread(()->{

HashMap<Integer, Integer> integerIntegerHashMap = new HashMap<>();

System.out.println(Thread.currentThread().getName()+"试图获取到锁1");

synchronized(loker1){

System.out.println(Thread.currentThread().getName()+"获取到锁1");

try {

Thread.sleep(3000);

System.out.println(Thread.currentThread().getName()+"试图获取到锁2");

synchronized (loker2){

System.out.println(Thread.currentThread().getName()+"获取到锁2");

}

} catch (InterruptedException e) {

throw new RuntimeException(e);

}


}

},"t2");

t1.start();

t2.start();


}

}

image.png

我们通过改变获取锁的顺序解决了死锁问题

④循环等待:最常见的解决死锁的策略:我们设计破解死锁的一套获取锁的策略

我们针对哲学家吃面问题,来给出恰当的解决策略:

我们给每个筷子编上编号,每个哲学家先拿自己小号的筷子,然后再拿大号的筷子,拿不到则进行等待。

image.png

一到四号哲学家分别拿起了一到四号筷子,但是五号哲学家相对较小的筷子是一号,这时候先不拿筷子,这时四号拿起了五号筷子,吃完了面,放下了一双筷子,然后三二一号哲学家一次拿筷子吃面放下筷子,这时候五号哲学家就可以吃面了,死锁问题也就解决了。

image.png

  • 银行家算法:首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。因此,状态包含两个向量Resource(系统中每种资源的总量)和Available(未分配给进程的每种资源的总量)及两个矩阵Claim(表示进程对资源的需求)和Allocation(表示当前分配给进程的资源)。安全状态是指至少有一个资源分配序列不会导致死锁。当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意这个请求;如果不是,阻塞该进程知道同意该请求后系统状态仍然是安全的。
相关文章
|
7月前
|
监控 算法 安全
|
4月前
死锁原因
死锁原因
50 1
|
7月前
|
安全 算法 程序员
|
7月前
|
SQL 存储 设计模式
如何与死锁斗争!!!
尽量不要改动线上数据库的字段,因为会触发锁表影响业务,严重时还可能出现死锁!数据库真的出现了死锁,业务全挂了,这种时候应该怎么办呢?本文就给大家分享一下数据库死锁的排查思路,万一出了问题,也有底气去解决。
65 1
|
7月前
|
安全 Java 测试技术
发生死锁怎么办
发生死锁怎么办
69 0
|
安全 算法
死锁的总结(1)
死锁的总结
36 0
|
算法 安全
死锁的总结(2)
死锁的总结
52 0
|
算法 调度
死锁的理解
死锁的理解
88 0
|
程序员 Linux 芯片