likes
comments
collection
share

【一文通关】Java多线程基础(7)- 死锁

作者站长头像
站长
· 阅读数 63

死锁相关的知识

可重入锁

在讲解死锁之前,我们先了解可重入锁

可重入锁是一种支持重复获取的锁,也称为递归锁。它允许同一个线程多次获取同一个锁,同时也保证在多个线程之间互斥访问共享资源。

在可重入锁中,当一个线程已经持有锁时,它可以再次获取这个锁,而不会被阻塞。当线程释放锁时,它需要释放相同数量的锁,这样其他线程才能获取到锁。

为什么需要可重入锁呢?

答案是防止死锁

一个典型的可重入锁的例子是一个类中的两个方法 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
评论
请登录