ReentrantLock分为公平锁和非公平锁,那底层分别是如何实现的
ReentrantLock在Java中是通过AbstractQueuedSynchronizer(AQS)框架实现的,它提供了公平锁(FairSync)和非公平锁(NonfairSync)两种模式。这两种锁的实现主要区别在于获取锁的策略。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
// 非公平锁
private static final ReentrantLock nonFairLock = new ReentrantLock();
// 公平锁
private static final ReentrantLock fairLock = new ReentrantLock(true); // 传入true参数表示使用公平锁
public static void main(String[] args) {
//demonstrateLock(nonFairLock, "Non-Fair Lock");
demonstrateLock(fairLock, "Fair Lock");
}
private static void demonstrateLock(ReentrantLock lock, String lockType) {
class Task implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " acquired " + lockType + ", iteration: " + i);
Thread.sleep(10); // 模拟持有锁时的工作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
}
Thread t1 = new Thread(new Task(), "Thread-1");
Thread t2 = new Thread(new Task(), "Thread-2");
t1.start();
t2.start();
try {
t1.join(); // 等待所有线程完成
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
公平锁(FairSync)的实现:

公平锁在尝试获取锁时,会首先检查队列中是否有其他等待的线程。如果有其他线程已经在等待队列中,那么新来的线程就会加入到队列的末尾排队等待,而不是尝试直接获取锁。这样可以确保线程按照它们请求锁的顺序来获取锁,体现了公平性。公平锁的实现步骤大致如下:
1. 调用`lock()`方法时,首先通过CAS操作尝试快速获取锁。
2. 如果失败,检查AQS的等待队列中是否已经有线程在等待。如果有,当前线程就加入到队列末尾等待,不会进行进一步的尝试。
3. 当前线程会进入阻塞状态,直到被AQS唤醒,通常是前一个持有锁的线程释放锁并唤醒它。

### 非公平锁(NonfairSync)的实现:

非公平锁在尝试获取锁时,不管队列中是否有其他线程在等待,总是会先尝试直接通过CAS操作快速获取锁,这可能导致新来的线程“插队”,获得锁的机会优于已经在队列中等待的线程。这种设计牺牲了公平性以换取更高的吞吐量。非公平锁的实现步骤大致如下:
1. 在`lock()`方法中,同样首先尝试通过CAS操作快速获取锁,即使此时有其他线程已经在队列中等待。
2. 如果快速获取失败,非公平锁仍然可能会继续尝试CAS获取锁,即使队列中已有等待线程。这意味着它可能在某些情况下直接获取锁,跳过队列中的其他线程。
3. 只有当多次尝试CAS获取锁都失败后,才会将当前线程加入到等待队列,并进入阻塞状态。

无论是公平锁还是非公平锁,它们都使用了AQS的内部FIFO队列来管理等待的线程,以及通过状态位(state)和等待节点(Node)来协调线程的阻塞与唤醒。此外,ReentrantLock还支持可重入特性,即已经持有锁的线程再次请求相同锁时可以直接获取,而不需要再次排队。

浙公网安备 33010602011771号