1. 先写 Node 节点(等待队列节点)
java
import java.util.concurrent.locks.LockSupport;
// 等待队列节点
class Node {
// 前驱节点
Node prev;
// 后继节点
Node next;
// 排队的线程
Thread thread;
public Node(Thread thread) {
this.thread = thread;
}
}
2. 手写极简 AQS(核心)
java
abstract class SimpleAQS {
// 锁状态:0=无锁,1=已加锁,>1 重入
private volatile int state;
// 队列头
private volatile Node head;
// 队列尾
private volatile Node tail;
// 持有锁的线程
private Thread exclusiveOwnerThread;
// ========== 原子操作 state ==========
protected final boolean compareAndSetState(int expect, int update) {
// 真实 JDK 用 unsafe CAS,这里简化理解
if (state == expect) {
state = update;
return true;
}
return false;
}
// ========== 入队逻辑(尾插) ==========
private Node enqueue() {
Node node = new Node(Thread.currentThread());
// 尾插
Node t = tail;
if (t == null) {
head = node;
tail = node;
} else {
node.prev = t;
tail = node;
t.next = node;
}
return node;
}
// ========== 阻塞等待 ==========
private void park(Node node) {
for (;;) {
Node p = node.prev;
// 如果前驱是头节点,再尝试抢一次锁
if (p == head && tryAcquire(1)) {
// 抢到了,把自己设为新 head
head = node;
node.prev = null;
node.thread = null;
return;
}
// 抢不到,阻塞线程
LockSupport.park(this);
// 被唤醒后继续循环抢锁
}
}
// ========== 解锁唤醒下一个 ==========
private void unparkSuccessor() {
Node h = head;
if (h != null && h.next != null) {
// 唤醒队首第一个等待线程
LockSupport.unpark(h.next.thread);
}
}
// ========== 模板方法(子类实现) ==========
protected abstract boolean tryAcquire(int arg);
protected abstract boolean tryRelease(int arg);
// ========== 对外加锁 ==========
public final void acquire(int arg) {
// 尝试加锁成功直接返回
if (!tryAcquire(arg)) {
// 失败 → 入队 + 阻塞
Node node = enqueue();
park(node);
}
}
// ========== 对外解锁 ==========
public final boolean release(int arg) {
if (tryRelease(arg)) {
// 唤醒后继
unparkSuccessor();
return true;
}
return false;
}
// ========== getter/setter ==========
public int getState() { return state; }
public void setState(int state) { this.state = state; }
public Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; }
public void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; }
}
3. 基于手写 AQS 实现 ReentrantLock
java
import java.util.concurrent.locks.Lock;
import java.util.Collection;
public class SimpleReentrantLock extends SimpleAQS implements Lock {
// ========== 加锁实现(非公平) ==========
@Override
protected boolean tryAcquire(int arg) {
Thread current = Thread.currentThread();
int c = getState();
// 1. 无锁,CAS 抢锁
if (c == 0) {
if (compareAndSetState(0, arg)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 2. 可重入:当前线程已经持有锁
else if (current == getExclusiveOwnerThread()) {
setState(c + arg);
return true;
}
// 3. 抢锁失败
return false;
}
// ========== 解锁实现 ==========
@Override
protected boolean tryRelease(int arg) {
int c = getState() - arg;
// 只有持有锁线程才能释放
if (Thread.currentThread() != getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
}
boolean free = false;
// state 减到 0,才算完全释放
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
// ========== Lock 接口实现 ==========
@Override
public void lock() {
acquire(1);
}
@Override
public void unlock() {
release(1);
}
// 下面这些方法简单实现,方便运行
@Override public void lockInterruptibly() {}
@Override public boolean tryLock() { return false; }
@Override public boolean tryLock(long time, java.util.concurrent.TimeUnit unit) { return false; }
@Override public java.util.concurrent.locks.Condition newCondition() { return null; }
}
4. 测试多线程安全(可直接运行)
java
public class TestLock {
static int count = 0;
public static void main(String[] args) throws InterruptedException {
SimpleReentrantLock lock = new SimpleReentrantLock();
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + count);
// 结果一定是 20000,线程安全
}
}
运行结果:
count = 20000
5. 极简总结(对应你手写的 AQS)
- AQS = state + 队列 + CAS
lock()→tryAcquire- 抢锁成功 → 直接执行
- 抢锁失败 → 入队 →
park阻塞
unlock()→tryRelease- state - 1
- state == 0 → 完全释放 → 唤醒队列第一个节点
- 可重入:同一个线程多次加锁 → state 累加
- 队列是双向链表,保证公平排队
6.AQS的核心思想
当前一个节点已经在临界区上运行时,就将它的下一个节点唤醒,其他节点还处于休眠状态,让它的下一个节点不断轮询,一旦当前节点释放,下一个节点就很容易抢到,不会与其他线程竞争。核心思想就是:
-
只有队列的第二个节点(head.next)会被唤醒
-
其他后面的节点全部在睡觉,不参与竞争
-
被唤醒的节点,会在循环里尝试抢锁
-
因为没人跟它抢,所以一抢一个准
-
抢到后,它变成新的 head,然后继续唤醒下一个
这就是 AQS 大名鼎鼎的:链式唤醒(唤醒一个,只唤醒队首第二个)、无竞争抢锁(只有一个线程抢锁)。
park() 只有队列第二个线程会抢锁其他线程都在睡觉,不竞争
unparkSuccessor() 只唤醒队列第二个线程不唤醒全部,不产生竞争