【一文通关】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
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值