利用aqs构建一个自己的公平独占锁
前期回顾
利用aqs构建一个自己的非公平独占锁
本期目标:实现一个公平独占锁
独占:同一时间只有一个线程能获取锁。
公平:线程按照请求顺序(FIFO)获得锁,避免线程“插队”。
基于 AQS:重写 tryAcquire 和 tryRelease 方法。
代码
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;/*** 基于 AQS 实现的公平独占锁*/
public class FairExclusiveLock implements Lock {// 内部静态类:继承 AQS,实现公平锁逻辑private static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 1L;// 是否为公平模式@Overrideprotected boolean isHeldExclusively() {return getExclusiveOwnerThread() == Thread.currentThread();}// 尝试获取锁(公平方式)@Overrideprotected boolean tryAcquire(int acquires) {if (acquires != 1) throw new IllegalArgumentException();// 【核心】检查是否有前驱等待节点(公平性关键)if (hasQueuedPredecessors()) {return false; // 有其他线程在等待,本次获取失败,加入队列}int currentStatus = getState();if (currentStatus == 0) {// 无锁状态,尝试 CAS 获取if (compareAndSetState(0, 1)) {// 设置锁的拥有者setExclusiveOwnerThread(Thread.currentThread());return true;}} else if (getExclusiveOwnerThread() == Thread.currentThread()) {// 已经是当前线程持有锁,支持可重入setState(currentStatus + 1);return true;}return false;}// 尝试释放锁@Overrideprotected boolean tryRelease(int releases) {if (Thread.currentThread() != getExclusiveOwnerThread()) {throw new IllegalMonitorStateException("Not owner");}int nextc = getState() - 1;boolean free = (nextc == 0);if (free) {setExclusiveOwnerThread(null);}setState(nextc);return free;}}// 使用 Sync 实例private final Sync sync = new Sync();// ------------- Lock 接口实现 -------------@Overridepublic void lock() {sync.acquire(1); // 阻塞式获取锁}@Overridepublic void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}@Overridepublic boolean tryLock() {return sync.tryAcquire(1);}@Overridepublic boolean tryLock(long time, java.util.concurrent.TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(time));}@Overridepublic void unlock() {sync.release(1); // 释放锁}@Overridepublic java.util.concurrent.Condition newCondition() {return sync.newCondition();}
}
公平性的实现
- hasQueuedPredecessors():判断当前线程前面是否有其他线程在等待。
- 如果有,即使锁空闲,也 不立即获取,而是排队等待。
- 这就是 公平锁 的核心机制。
测试代码
public class FairExclusiveLockTest {private static final FairExclusiveLock lock = new FairExclusiveLock();private static int counter = 0;public static void main(String[] args) throws InterruptedException {Thread[] threads = new Thread[5];for (int i = 0; i < 5; i++) {final int threadId = i;threads[i] = new Thread(() -> {System.out.println("线程 " + threadId + " 尝试获取锁");lock.lock();try {System.out.println("线程 " + threadId + " 获取锁,执行任务...");Thread.sleep(1000); // 模拟工作counter++;System.out.println("线程 " + threadId + " 完成,counter=" + counter);} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {lock.unlock();}});}// 启动所有线程for (Thread t : threads) t.start();for (Thread t : threads) t.join();System.out.println("最终 counter = " + counter);}
}
输出结果
线程 0 尝试获取锁
线程 1 尝试获取锁
线程 2 尝试获取锁
线程 3 尝试获取锁
线程 4 尝试获取锁
线程 0 获取锁,执行任务...
线程 0 完成,counter=1
线程 1 获取锁,执行任务...
线程 1 完成,counter=2
线程 2 获取锁,执行任务...
...
