AQS和ReentrantLock源码解析

本文深入探讨了AQS的内部结构和工作原理,包括独占模式和共享模式的资源获取与释放机制,以及ConditionObject的实现。进一步解析了ReentrantLock的Sync类,非公平锁和公平锁的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AQS和ReentrantLock源码解析

1. AQS

AQS实现的是排队和阻塞的机制。内部使用了Node节点的链表(CLH队列),AQS是JUC的基础。

1. 内部结构

它的同步方式有两种:

  • 独占模式:资源是独占的,一次只能一个线程获取。
  • 共享模式:同时可以被多个线程获取。

这两种模式都定义在Node节点里面,下面是Node节点的源码:

static final class Node {
        static final Node SHARED = new Node(); 共享标记
        static final Node EXCLUSIVE = null; 独占标记
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1; 表示后继节点需要被唤醒
        static final int CONDITION = -2; 表示等待某个条件
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev;
        volatile Node next;
        volatile Thread thread; 对应的线程
        Node nextWaiter; 等待队列里下一个等待的节点
        
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
        
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

可以看出里面封装了线程。这里注意一下,通过Node节点可以实现两个队列,一个是prev和next实现的同步队列(双向的);另一个是nextWaiter实现的等待队列(单向的,这个主要用在Condition条件上)

AQS的设计是基于模板方法模式,具体实现交给子类,可以按需要实现相应的方法

	独占锁获取
	protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
    独占锁释放
	protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
    共享锁获取
	protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }
    共享锁释放
  	protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }  

2. 资源的获取

资源的获取方法是acquire(int arg),arg是资源数量。线程在进入同步队列前还有一次获得锁的机会,这也是后面ReentrantLock公平锁实现的关键。

	public final void acquire(int arg) {
		在进入同步队列前还有一次锁获取的机会
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

首先调用tryAcquire方法尝试获得资源(需要子类实现),如果获得资源失败了就会调用addWaiter方法把当前线程放到同步队列的尾节点,传入的Node参数是独占的,下面是addWaiter方法的源码:

	private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    通过自旋CAS更新尾结点
	private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
	

addWaiter方法会先尝试一次快速的CAS更新尾结点,如果没有成功的话就调用enq方法进行自旋CAS去更新尾结点。调用addWaiter方法把新节点放到队列尾后,就会调用acquireQueued方法,从头结点开始获得资源,下面是该方法的源码:

	final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                如果当前节点的前驱是头节点,说明当前节点是第二个节点,可以获取资源
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                如果可以休息就进入waiting状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

parkAndCheckinterrupt方法内部调用了LockSupport类的park方法来阻塞当前线程,unpark方法则是停止阻塞。shouldParkAfterFailedAcquire方法其实就是要保证当前节点的前驱结点是SIGNAL状态,等到资源释放的时候就会调用unpark方法把后继节点唤醒。有兴趣的可以自己查看源码,这里就不讨论了。
节点进入了队列之后,如果获取资源失败就会被阻塞,只有头结点是活跃的,资源释放的时候会唤醒它的后继节点。
这里只讨论了独占资源获取的方法,其他的共享资源获取和可中断获取就不一一讨论了,有兴趣可自行查看源码。

3. 资源释放

资源的释放相对来说就简单很多了,前面也有提到过,下面还是源码:

	public final boolean release(int arg) {
		尝试释放资源,还是子类去实现
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

	private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
        	唤醒线程
            LockSupport.unpark(s.thread);
    }

4. AQS的Condition
AQS的条件阻塞和唤醒是基于ConditionObject来实现的,可以通过await方法进行阻塞,signal方法唤醒线程,下面是它的部分源码:

 public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        条件队列的头节点 
        private transient Node firstWaiter;
        条件队列的尾节点 
        private transient Node lastWaiter;
}

ConditionObject只定义了头结点和尾结点,等待队列的next节点其实就是Node节点的nextWaiter,这个前面有说过,下面就简单分析一下它的await方法和signal方法。

	first传进来的其实就是等待队列的头结点
	private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
   
	final boolean transferForSignal(Node node) {
       
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        把节点放到同步队列的尾部,这个方法前面有说过
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        	唤醒线程
            LockSupport.unpark(node.thread);
        return true;
    }

//await方法
	public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            把线程放到等待队列的尾部
            Node node = addConditionWaiter();
            释放锁
            long savedState = fullyRelease(node);
            int interruptMode = 0;
            判断是否在同步队列里,如果不是就阻塞线程
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }
	

2. ReentrantLock

1. Sync类
AQS讲完了,就可以开始讲ReentrantLock了,前面有说过AQS是基于模板方法模式的,有部分方法并没有实现,而是留给了子类去实现,Sync则是它的子类,而Sync的lock方法又留给了子类NonfairSync 和FairSync 去实现,这个两个类是ReentrantLock公平锁和非公平锁的基础。下面简单给出Sync类的代码:

	abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        留给子类实现对应的加锁
        abstract void lock();
        
		非公平锁的获取
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            无锁获取,通过CAS获取锁
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                	把锁的拥有者换成当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            判断锁的拥有者是否为当前线程,实现锁的重入
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        
		锁的释放,公平锁和非公平锁是一样的
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }

非公平锁的获取其实还是调用AQS的acquire方法,不公平在于同步队列只有头结点可以争锁,但是其他还没进入同步队列的线程也可以争,这样就对同步队列里的其他线程不公平,源码也十分的简单

	static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        
        如果无锁则通过CAS获取,失败了就进入同步队列里等待锁
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        调用父类的非公平锁获取方法
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

公平锁:老的线程排队使用锁,新的线程仍然要进入同步队列里排队使用锁

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

		没有非公平锁那样的CAS操作,全部都要放到同步队列里
        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            	这里判断队列是否还有节点也是公平锁的保证
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值