【一文通关】Java多线程基础(7)- 死锁
死锁相关的知识
可重入锁
在讲解死锁之前,我们先了解可重入锁
可重入锁是一种支持重复获取的锁,也称为递归锁。它允许同一个线程多次获取同一个锁,同时也保证在多个线程之间互斥访问共享资源。
在可重入锁中,当一个线程已经持有锁时,它可以再次获取这个锁,而不会被阻塞。当线程释放锁时,它需要释放相同数量的锁,这样其他线程才能获取到锁。
为什么需要可重入锁呢?
答案是防止死锁
一个典型的可重入锁的例子是一个类中的两个方法 A 和 B,它们都需要获取同一个锁来访问共享资源,且方法 A 可以调用方法 B,而不会造成死锁。
下面是一个使用 synchronized 关键字实现的可重入锁的例子:
public class ReentrantLockExample {
private final Object lock = new Object();
private int count = 0;
public void methodA() {
synchronized (lock) {
System.out.println("Method A is called");
// 调用 methodB,不会导致死锁
methodB();
}
}
public void methodB() {
synchronized (lock) {
System.out.println("Method B is called");
count++;
}
}
}
在这个例子中,类 ReentrantLockExample 中的两个方法 methodA 和 methodB 都使用了 synchronized 关键字来获取同一个锁 lock。当一个线程执行 methodA 方法时,它会获取 lock 的锁,然后调用 methodB 方法,此时由于线程已经持有锁,因此可以再次获取锁,而不会被阻塞。当 methodB 执行完毕后,线程会释放两次锁,然后 methodA 继续执行。
这个例子展示了可重入锁的一个重要特性,即同一个线程在调用同一个对象的可重入方法时,不会造成死锁。这种特性可以提高程序的效率,同时也可以避免死锁的发生。
那什么时候会发生死锁呢?
在编程中,死锁往往发生在多个线程之间竞争共享资源的情况下。例如,在一个程序中,如果两个或多个线程尝试获取相同的锁,但是获取锁的顺序不同,就可能发生死锁。如果一个线程获取了锁 A,然后尝试获取锁 B,而另一个线程已经获取了锁 B,然后尝试获取锁 A,这就会导致死锁的发生。
因此,在编程中应该尽量避免死锁的发生。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Counter());
thread1.setName("sss");
Thread thread2 = new Thread(new Counter());
thread2.setName("bbb");
thread1.start();
thread2.start();
}
}
class Counter implements Runnable {
int count = 0;
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
@Override
public void run() {
String name = Thread.currentThread().getName();
if(name.equals("sss")){
add();
} else if (name.equals("bbb")) {
reduce();
}
}
public void add() {
synchronized (lock1){
System.out.println(Thread.currentThread().getName() + "获得了lock1锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "等待lock2.....");
synchronized (lock2){
count++;
System.out.println(count);
}
}
}
public void reduce(){
synchronized (lock2){
System.out.println(Thread.currentThread().getName() + "获得了lock2锁");
System.out.println(Thread.currentThread().getName() + "等待lock1.....");
synchronized (lock1){
count--;
System.out.println(count);
}
}
}
}
运行以上代码就可以观察到死锁的发生,JVM一直退出不了。
解决死锁
死锁发生之后,是没办法自动解决的,只能手动终止JVM。
一般来说,可以通过合理的锁设计、避免锁的嵌套、避免长时间占用锁、避免循环等待等方式来避免死锁的发生。
转载自:https://juejin.cn/post/7229517949970104380