ReentrantLock、Semaphore、CountDownLatch 背后都是 AQS。继承它只需要实现几个 try 方法,排队、阻塞、唤醒由框架负责。这篇亲手写四个同步器并运行,验证独占与共享模式、公平性和 Condition 的行为。
4 个线程各加锁 50000 次,计数 = 200000(期望 200000);结束时 state = 0,hasQueuedThreads = false 持有锁时:state = 1,isHeldExclusively = true,AQS.toString() = …Sync@…[State = 1, empty queue] 同一线程再次 lock(不可重入):tryAcquireNanos(300 ms) → false(耗时 303 ms);这是一把不可重入锁,同线程第二次 lock() 会死锁 没有持有时 unlock → IllegalMonitorStateException: 不是持有者(这是 tryRelease 里自己抛的) 另一个线程 acquireInterruptibly 阻塞中:getQueueLength = 1,队列里的线程状态 = WAITING 中断它 → InterruptedException;之后 getQueueLength = 0(取消的节点被清理)
3 个许可、12 个线程各 20 次:同时持有许可的线程数最大 = 3(应 ≤ 3),结束时 state = 3 共享模式闩锁 Gate(3),5 个线程在 await:getQueueLength = 5,已通过 0 countDown 2 次后:state = 1,已通过 0 第 3 次 countDown(state 变 0)之后,5 个等待者全部通过,——一次 releaseShared 唤醒了队列里所有的共享等待者(传播),已通过 5 Sem(0),4 个线程等待:release 1 个许可后通过 1 个;再 releaseShared(3) 后共通过 4 个
主线程持有锁,W0..W3 依次(间隔 30 ms)来排队:getQueuedThreads() 返回的顺序 = [W3, W2, W1, W0](文档不保证顺序,这里是最近排队的在前);hasQueuedPredecessors() = true(对一个新来的线程来说前面有人) 非公平 tryAcquire(直接 CAS):8 个线程循环加锁 1.5 s,总次数 155738178,最多的线程 29733070 次,最少的线程 14646912 次(最多/最少 = 2.0 倍) 公平 tryAcquire(先 hasQueuedPredecessors):8 个线程循环加锁 1.5 s,总次数 642869,最多的线程 80440 次,最少的线程 80334 次(最多/最少 = 1.0 倍)
用 AQS 的 ConditionObject 写容量 4 的有界缓冲区,生产 20000 个、消费求和 = 200010000(期望 200010000) 没有持有锁就 signal → IllegalMonitorStateException(来自 isHeldExclusively 返回 false) 另一个线程拿到锁并 await 之后:state = 0(await 把锁完全释放了,所以主线程现在能拿到锁) 主线程持有锁时:hasWaiters = true,getWaitQueueLength = 1,锁的等待队列长度 getQueueLength = 0 signal 之后、主线程释放锁之前:被唤醒的线程被移到锁的等待队列:getQueueLength = 1,getWaitQueueLength = 0 主线程释放锁之后,被唤醒的线程拿到锁并结束:存活 = false