一, ReentrantLock 和synchronized
ReentrantLock和synchronized都是Java中用于实现线程同步的机制,但它们有一些不同之处。
-
可重入性:ReentrantLock是可重入锁,也就是说同一个线程可以多次获取同一把锁,而不会被自己所持有的锁所阻塞。而synchronized也是可重入的,同一个线程可以多次获取同一把锁,但是需要注意的是,如果使用synchronized嵌套锁,会导致死锁的发生。
-
锁的获取方式:ReentrantLock提供了多种获取锁的方式,如lock()、tryLock()和tryLock(long time, TimeUnit unit)等。而synchronized关键字只有一种获取锁的方式,即使用synchronized关键字修饰的代码块或方法,当线程进入synchronized代码块或方法时,会自动获取锁。
-
公平性:ReentrantLock可以通过构造函数来指定是否为公平锁。公平锁会按照线程的申请顺序来获取锁,而非公平锁则是无序的。而synchronized关键字是非公平锁,无法设置为公平锁。
-
可中断性:在等待锁的过程中,ReentrantLock可以通过调用lockInterruptibly()方法来响应中断请求,而synchronized关键字在等待锁的过程中无法响应中断请求。
-
条件变量:ReentrantLock提供了Condition接口的实现类Condition,可以通过Condition对象来实现线程的等待和唤醒。而synchronized关键字没有提供类似的功能。
总的来说,ReentrantLock相对于synchronized关键字来说,提供了更多的灵活性和功能。它可以更加精确地控制锁的获取和释放,支持公平锁和非公平锁,提供了可中断性和条件变量等特性。但是,使用ReentrantLock需要手动进行锁的获取和释放,相对于synchronized关键字来说,使用起来会稍微复杂一些。因此,在选择使用ReentrantLock还是synchronized时,需要根据具体的需求和场景来进行选择。
二, ReentrantLock lock()、tryLock()
ReentrantLock提供了两种常用的获取锁的方式,分别是lock()和tryLock()。
-
lock()方法:
lock()方法是ReentrantLock类中的一个阻塞方法,用于获取锁。如果当前锁没有被其他线程持有,则当前线程会立即获取到锁,并继续执行后续代码。如果当前锁被其他线程持有,则当前线程会被阻塞,直到获取到锁为止。例如:
ReentrantLock lock = new ReentrantLock(); lock.lock(); // 获取锁 try { // 执行业务逻辑 } finally { lock.unlock(); // 释放锁 }
在使用lock()方法获取锁时,需要注意在finally块中释放锁,以确保锁的释放不受异常的影响。
-
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提供了更多的灵活性和功能。它具有以下特点:
-
可重入性:同一个线程可以多次获取同一把锁,而不会被自己所持有的锁所阻塞。这种机制使得ReentrantLock可以支持递归调用或者是嵌套调用。
-
公平性:ReentrantLock可以通过构造函数来指定是否为公平锁。公平锁会按照线程的申请顺序来获取锁,而非公平锁则是无序的。
-
可中断性:在等待锁的过程中,可以通过调用lockInterruptibly()方法来响应中断请求,而不是一直等待下去。
-
条件变量:ReentrantLock提供了Condition接口的实现类Condition,可以通过Condition对象来实现线程的等待和唤醒。
-
锁的获取方式: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命令查看线程堆栈信息,或者使用工具分析线程转储文件等。
五 公平锁有啥用
公平锁是一种锁的获取机制,它可以按照线程的申请顺序来获取锁资源。在多线程环境下,公平锁可以保证所有线程都有公平的机会获取到锁,避免某些线程长时间等待锁资源的情况。
公平锁的主要作用如下:
-
公平性:公平锁可以保证线程按照申请的顺序获取锁,避免线程饥饿现象的发生。所有线程都有平等的机会获取锁资源,提高了系统的公平性。
-
避免线程饥饿:在使用非公平锁的情况下,如果某个线程频繁地申请锁资源,那么其他线程可能会一直等待,导致饥饿现象的发生。而使用公平锁可以避免这种情况,保证每个线程都有获取锁的机会。
-
避免锁的长时间占用:在使用非公平锁的情况下,某个线程可能会连续多次获取到锁资源,导致其他线程长时间等待。而使用公平锁可以保证每个线程都有机会获取锁,避免某个线程长时间占用锁资源。
需要注意的是,公平锁相对于非公平锁来说,会带来一些额外的开销,因为需要维护一个等待队列来记录等待锁资源的线程。在高并发的情况下,公平锁的性能可能会受到一定的影响。因此,在选择使用公平锁还是非公平锁时,需要根据具体的业务场景和性能需求来进行选择。