死锁是指两个或多个线程在互相等待对方释放锁的状态,从而导致程序无法继续执行的情况。
在Java多线程中,死锁通常是由于以下四种情况的组合所导致的:
- 互斥:多个线程竞争同一资源(如锁),每次只能有一个线程占用,其他线程必须等待。
- 占有且等待:线程在持有锁的同时,等待其他线程持有的锁。
- 不可抢占:已经获取锁的线程不能被其他线程强制中断或释放锁。
- 循环等待:多个线程形成一个循环等待的关系,每个线程都在等待其他线程释放锁。
要解决死锁问题,可以采取以下措施:
- 避免使用多个锁:尽量使用单个锁或者使用更高级别的同步机制,比如并发集合。
- 统一获取锁的顺序:确保所有线程获取锁的顺序一致,避免出现循环等待的情况。
- 尽可能缩小同步代码块:减少同步代码块的长度,缩小互斥的范围。
- 及时释放锁:尽可能早地释放锁,避免占有且等待的情况。
- 使用定时锁:在获取锁时,使用带有超时时间的锁,避免因为等待锁而导致死锁。
- 强制中断线程:在发现死锁时,可以强制中断其中一个线程,打破循环等待的环。
需要注意的是,死锁是一个复杂的问题,解决起来也比较困难,需要仔细分析代码和调试。
public class DeadLock {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread t1 = new Thread(() -> {
// 1.占有一把锁
synchronized (lockA) {
System.out.println("线程1获得锁A");
// 休眠1s(让线程2有时间先占有锁B)
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取线程2的锁B
synchronized (lockB) {
System.out.println("线程1获得锁B");
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
// 占B锁
synchronized (lockB) {
System.out.println("线程2获得锁B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取线程1的锁A
synchronized (lockA) {
System.out.println("线程2获得了锁A");
}
}
});
t2.start();
}
}
运行结果:
这段代码是一个典型的死锁示例,它包含两个线程,每个线程都持有一个锁并等待另一个锁的释放,从而导致死锁。
具体来说,这里定义了两个对象锁 lockA 和 lockB,并在两个线程中分别获取这两个锁。当线程1获取锁A后,它会休眠1秒钟,然后尝试获取锁B;而当线程2获取锁A后,它也会休眠1秒钟,然后尝试获取锁B。由于两个线程都在等待对方释放锁,因此它们会一直阻塞在获取锁的代码块中,无法继续执行,从而形成了死锁。
要解决这个问题,可以按照避免死锁的几种方法之一,例如对锁的获取顺序进行统一、尽量缩小同步代码块的范围、及时释放锁等。下面是一种修改方案:
public class UnDeadLock2 {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread t1 = new Thread(() -> {
synchronized (lockA) {
System.out.println("线程1得到锁A");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println("线程1得到锁B");
System.out.println("线程1释放锁B");
}
System.out.println("线程1释放锁A");
}
}, "线程1");
t1.start();
Thread t2 = new Thread(() -> {
synchronized (lockA) {
System.out.println("线程2得到锁A");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println("线程2得到锁B");
System.out.println("线程2释放锁B");
}
System.out.println("线程2释放锁A");
}
}, "线程2");
t2.start();
}
}
在修改后的代码中,两个线程都按照相同的顺序获取锁(先获取锁A,再获取锁B),从而避免了死锁问题。此外,线程2也及时释放了锁A,避免了占有且等待的情况。
运行结果:
标签:yyds,Thread,System,死锁,干货,线程,println,out From: https://blog.51cto.com/u_11365839/6934242