线程死锁.
上一篇:线程中断
线程死锁
定义:死锁指的是两个及两个以上的线程在执行中,因争夺资源而造成的互相等待的现象,在无外力的作用下这些线程会一直等待而无法继续执行。
死锁产生条件:
1、互斥条件:对象被一个线程占用,那么其他线程要等这个线程用完才能去占用;
2、请求并持有条件:一个线程占用了一个资源,执行过程中还去请求其他的资源;
3、不可剥夺条件:一个对象一旦被某个线程占用,必须要等到这个线程释放这个对象的锁,其他线程才可以去竞争;
4、环路等待条件:发生死锁时,必然存在“线程-资源”环形链,就是说必定线程B在等待线程A所持有的资源;
避免死锁的方法:破坏死锁产生的四个条件中的至少一个;由于操作系统限制,目前只能破坏【请求并持有条件】和【环路等待条件】
死锁代码示例:
package com.dwk.thread;
/**
* @author duweikun
* @describe 线程死锁测试类
* @date 2021/10/15
*/
public class DeadLockTest {
private Object A = new Object();
private Object B = new Object();
public void test(){
Thread thread1 = new Thread(() -> {
try {
//为了让线程2先占用对象B,线程1先休眠1秒
Thread.sleep(1000);
synchronized (A){
System.out.println("线程1占用对象A");
synchronized (B){
System.out.println("线程1占用对象B");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
synchronized (B) {
System.out.println("线程2占用对象B");
//为了让线程1先占用对象A,让线程2先休眠1秒
Thread.sleep(1000);
synchronized (A) {
System.out.println("线程2占用对象A");
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
});
thread1.start();
thread2.start();
}
public static void main(String[] args) {
DeadLockTest deadLockTest = new DeadLockTest();
deadLockTest.test();
}
}
执行结果:
看idea的运行标志,它会一直保持运行,这两个线程在互相等待对方释放自己需要的对象这样就造成了死锁,两个线程都无法继续往下执行,不但造成逻辑无法执行还浪费cpu资源;
以上代码要避免死锁,
1、可以让线程2先请求对象A再请求对象B,,即保持资源申请的有序性,这样可以同时破坏请求并持有条件和环路等待条件;
package com.dwk.thread;
/**
* @author duweikun
* @describe 线程死锁测试类
* @date 2021/10/15
*/
public class DeadLockTest {
private Object A = new Object();
private Object B = new Object();
public void test(){
//两个线程请求A、B的顺序一致
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000);
synchronized (A){
System.out.println("线程1占用对象A");
synchronized (B){
System.out.println("线程1占用对象B");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
synchronized (A) {
System.out.println("线程2占用对象A");
Thread.sleep(1000);
synchronized (B) {
System.out.println("线程2占用对象B");
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
});
thread1.start();
thread2.start();
}
public static void main(String[] args) {
DeadLockTest deadLockTest = new DeadLockTest();
deadLockTest.test();
}
}
2、那么在请求另外一个对象前,先释放自己所持有的对象的锁也是可以的,即破坏请求并持有条件;
package com.dwk.thread;
/**
* @author duweikun
* @describe 线程死锁测试类
* @date 2021/10/15
*/
public class DeadLockTest {
private Object A = new Object();
private Object B = new Object();
public void test(){
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000);
synchronized (A){
System.out.println("线程1占用对象A");
synchronized (B){
System.out.println("线程1占用对象B");
//唤醒B阻塞队列中的所有等待线程,即唤醒thread2
B.notifyAll();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
synchronized (B) {
System.out.println("线程2占用对象B");
//释放对象B的锁,thread2进入阻塞队列
B.wait();
synchronized (A) {
System.out.println("线程2占用对象A");
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
});
thread1.start();
thread2.start();
}
public static void main(String[] args) {
DeadLockTest deadLockTest = new DeadLockTest();
deadLockTest.test();
}
}
两种方法的执行结果如下:
总结:造成死锁的原因和申请资源的顺序有很大关系,使用资源申请的有序性原则就可以避免死锁,简单点说就是同步的概念,等一个用完了下一个再申请;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南