ReentrantLock 死锁

一, ReentrantLock 和synchronized

ReentrantLock和synchronized都是Java中用于实现线程同步的机制,但它们有一些不同之处。

  1. 可重入性:ReentrantLock是可重入锁,也就是说同一个线程可以多次获取同一把锁,而不会被自己所持有的锁所阻塞。而synchronized也是可重入的,同一个线程可以多次获取同一把锁,但是需要注意的是,如果使用synchronized嵌套锁,会导致死锁的发生。

  2. 锁的获取方式:ReentrantLock提供了多种获取锁的方式,如lock()、tryLock()和tryLock(long time, TimeUnit unit)等。而synchronized关键字只有一种获取锁的方式,即使用synchronized关键字修饰的代码块或方法,当线程进入synchronized代码块或方法时,会自动获取锁。

  3. 公平性:ReentrantLock可以通过构造函数来指定是否为公平锁。公平锁会按照线程的申请顺序来获取锁,而非公平锁则是无序的。而synchronized关键字是非公平锁,无法设置为公平锁。

  4. 可中断性:在等待锁的过程中,ReentrantLock可以通过调用lockInterruptibly()方法来响应中断请求,而synchronized关键字在等待锁的过程中无法响应中断请求。

  5. 条件变量:ReentrantLock提供了Condition接口的实现类Condition,可以通过Condition对象来实现线程的等待和唤醒。而synchronized关键字没有提供类似的功能。

总的来说,ReentrantLock相对于synchronized关键字来说,提供了更多的灵活性和功能。它可以更加精确地控制锁的获取和释放,支持公平锁和非公平锁,提供了可中断性和条件变量等特性。但是,使用ReentrantLock需要手动进行锁的获取和释放,相对于synchronized关键字来说,使用起来会稍微复杂一些。因此,在选择使用ReentrantLock还是synchronized时,需要根据具体的需求和场景来进行选择。

二, ReentrantLock lock()、tryLock()

ReentrantLock提供了两种常用的获取锁的方式,分别是lock()和tryLock()。

  1. lock()方法:
    lock()方法是ReentrantLock类中的一个阻塞方法,用于获取锁。如果当前锁没有被其他线程持有,则当前线程会立即获取到锁,并继续执行后续代码。如果当前锁被其他线程持有,则当前线程会被阻塞,直到获取到锁为止。

    例如:

    ReentrantLock lock = new ReentrantLock();
    lock.lock(); // 获取锁
    try {
        // 执行业务逻辑
    } finally {
        lock.unlock(); // 释放锁
    }
    

    在使用lock()方法获取锁时,需要注意在finally块中释放锁,以确保锁的释放不受异常的影响。

  2. tryLock()方法:
    tryLock()方法是ReentrantLock类中的一个非阻塞方法,用于尝试获取锁。如果当前锁没有被其他线程持有,则当前线程会立即获取到锁,并返回true。如果当前锁被其他线程持有,则当前线程不会被阻塞,而是立即返回false。

    例如:

    ReentrantLock lock = new ReentrantLock();
    if (lock.tryLock()) { // 尝试获取锁
        try {
            // 执行业务逻辑
        } finally {
            lock.unlock(); // 释放锁
        }
    } else {
        // 未获取到锁的处理逻辑
    }
    

    tryLock()方法可以用于避免线程被长时间阻塞,可以在获取锁失败后进行其他处理,而不是一直等待获取锁。

需要注意的是,无论是使用lock()方法还是tryLock()方法,都需要在合适的时候释放锁,以避免死锁或资源泄漏的问题。在使用tryLock()方法时,可以结合使用tryLock(long time, TimeUnit unit)方法来设置获取锁的超时时间,以避免长时间的等待。

三, ReentrantLock 是 Java 的 JUC(java.util.concurrent)包中提供的一种可重入锁,是一种递归无阻塞的同步机制

ReentrantLock是Java中JUC(java.util.concurrent)包中提供的一种可重入锁(Reentrant Lock),它是一种独占锁,也就是同一时间只能有一个线程持有该锁。

与传统的synchronized关键字相比,ReentrantLock提供了更多的灵活性和功能。它具有以下特点:

  1. 可重入性:同一个线程可以多次获取同一把锁,而不会被自己所持有的锁所阻塞。这种机制使得ReentrantLock可以支持递归调用或者是嵌套调用。

  2. 公平性:ReentrantLock可以通过构造函数来指定是否为公平锁。公平锁会按照线程的申请顺序来获取锁,而非公平锁则是无序的。

  3. 可中断性:在等待锁的过程中,可以通过调用lockInterruptibly()方法来响应中断请求,而不是一直等待下去。

  4. 条件变量:ReentrantLock提供了Condition接口的实现类Condition,可以通过Condition对象来实现线程的等待和唤醒。

  5. 锁的获取方式:ReentrantLock提供了多种获取锁的方式,如lock()、tryLock()和tryLock(long time, TimeUnit unit)等。

ReentrantLock是一种递归无阻塞的同步机制,它提供了更多的灵活性和功能,可以更加精确地控制多线程的并发访问。但是,使用ReentrantLock需要手动进行锁的获取和释放,相对于synchronized关键字来说,使用起来会稍微复杂一些。因此,在使用ReentrantLock时需要谨慎处理锁的获取和释放,以避免死锁等问题的发生。

四, 在使用ReentrantLock时,如果不正确地处理锁的获取和释放,就有可能导致死锁的问题。

死锁是指两个或多个线程相互等待对方释放锁资源,从而导致所有线程都无法继续执行的情况。

下面是一个可能导致死锁的示例代码:

import java.util.concurrent.locks.ReentrantLock;

public class DeadlockExample {
    private static ReentrantLock lock1 = new ReentrantLock();
    private static ReentrantLock lock2 = new ReentrantLock();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            lock1.lock();
            try {
                Thread.sleep(1000);
                lock2.lock();
                // 执行业务逻辑
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock2.unlock();
                lock1.unlock();
            }
        });

        Thread thread2 = new Thread(() -> {
            lock2.lock();
            try {
                Thread.sleep(1000);
                lock1.lock();
                // 执行业务逻辑
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock1.unlock();
                lock2.unlock();
            }
        });

        thread1.start();
        thread2.start();
    }
}

在上述代码中,有两个线程分别获取lock1和lock2两个锁。线程1先获取lock1,然后休眠1秒钟,再尝试获取lock2;线程2先获取lock2,然后休眠1秒钟,再尝试获取lock1。如果这两个线程同时运行,就有可能导致死锁的发生,因为线程1持有lock1而等待lock2的释放,而线程2持有lock2而等待lock1的释放,从而造成了相互等待的情况。

为了避免死锁的发生,我们需要合理地设计锁的获取和释放顺序,避免出现循环等待的情况。此外,还可以使用tryLock()方法来尝试获取锁,并设置超时时间,以避免长时间的等待。在发生死锁时,可以通过工具来检测和解决死锁问题,如使用jstack命令查看线程堆栈信息,或者使用工具分析线程转储文件等。

五 公平锁有啥用

公平锁是一种锁的获取机制,它可以按照线程的申请顺序来获取锁资源。在多线程环境下,公平锁可以保证所有线程都有公平的机会获取到锁,避免某些线程长时间等待锁资源的情况。

公平锁的主要作用如下:

  1. 公平性:公平锁可以保证线程按照申请的顺序获取锁,避免线程饥饿现象的发生。所有线程都有平等的机会获取锁资源,提高了系统的公平性。

  2. 避免线程饥饿:在使用非公平锁的情况下,如果某个线程频繁地申请锁资源,那么其他线程可能会一直等待,导致饥饿现象的发生。而使用公平锁可以避免这种情况,保证每个线程都有获取锁的机会。

  3. 避免锁的长时间占用:在使用非公平锁的情况下,某个线程可能会连续多次获取到锁资源,导致其他线程长时间等待。而使用公平锁可以保证每个线程都有机会获取锁,避免某个线程长时间占用锁资源。

需要注意的是,公平锁相对于非公平锁来说,会带来一些额外的开销,因为需要维护一个等待队列来记录等待锁资源的线程。在高并发的情况下,公平锁的性能可能会受到一定的影响。因此,在选择使用公平锁还是非公平锁时,需要根据具体的业务场景和性能需求来进行选择。

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值