可重入锁为什么取名为ReentrantLock?
"Reentrant"这个词来源于英文单词“re-enter”,意味着“重新进入”或“再次进入”。在编程术语中,它描述了一种锁机制,允许同一个线程多次获取同一个锁。因此,"ReentrantLock"这个名称直接反映了这个锁的核心特性,即允许线程重新进入并获取它已经持有的锁。
- 什么是可重入锁(ReentrantLock)?
可重入锁(ReentrantLock) 是 Java 中的一个显式锁,位于 java.util.concurrent.locks 包下,提供了一种显式控制线程访问共享资源的方式。它属于 Lock 接口的一种实现,可以替代传统的 synchronized 关键字,提供更细粒度的锁控制。
“可重入”的意思是:如果一个线程已经获取了锁,它可以再次获取这个锁而不会被阻塞,这就类似于 synchronized 关键字在同一个线程内可重复进入的行为。换句话说,如果同一个线程已经持有了锁,并再次尝试获取该锁,ReentrantLock 会允许这样做,而不会导致死锁。
ReentrantLock 的特点
可重入性:正如其名称,ReentrantLock 是一个可重入的锁,意味着持有锁的线程可以多次获取该锁,而不会引发阻塞。(底层通过CAS实现)
手动释放:与 synchronized 不同,ReentrantLock 需要显式调用 lock() 和 unlock() 方法来获取和释放锁。这使得它更加灵活,但也更容易出错(如忘记释放锁)。
公平锁与非公平锁:ReentrantLock 提供了公平锁和非公平锁两种方式:
公平锁:按线程请求的顺序来获取锁,先请求先得到(公平的含义)(通过AQS中的CLH实现),避免线程“饥饿”。
非公平锁(默认):不保证锁的获取顺序,系统会随机选择一个等待线程以获取锁,可能具有更高的性能。
中断响应:ReentrantLock 可以响应中断,允许等待锁的线程在被中断时终止等待。
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
尝试锁定:tryLock() 方法允许线程在无法获得锁时立即返回,而不是一直等待。
可重入锁与 synchronized 的区别
控制粒度
synchronized 是一种隐式锁,它通过修饰代码块或方法来实现同步,一旦进入同步块,锁的获取与释放都是由 JVM 自动管理。
ReentrantLock 是一种显式锁,需要手动调用 lock() 和 unlock() 来获取和释放锁,这样的显式控制可以提供更灵活的同步策略。
公平性
synchronized 的锁机制是非公平的,意味着等待的线程可能被任意安排。
ReentrantLock 支持公平锁机制,可以通过构造参数来选择是否公平。
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
中断响应与尝试获取锁
使用 synchronized 时,等待的线程无法响应中断。
使用 ReentrantLock,线程可以通过 lockInterruptibly() 方法响应中断,避免长时间阻塞。此外,可以通过 tryLock() 方法尝试获取锁,避免长时间等待。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void sharedResource() {
lock.lock(); // 获取锁
try {
System.out.println(Thread.currentThread().getName() + " is executing");
Thread.sleep(1000); // 模拟一些工作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 必须在 finally 中释放锁
}
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
Runnable task = example::sharedResource;
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
参考文章:
Comments 1 条评论