ReentrantLock源码解析(一)----- lock和unlock方法

本文深入剖析了ReentrantLock的构造、lock/unlock方法,特别是公平锁(FairSync)与非公平锁(NonfairSync)的tryAcquire实现,以及AQS中的acquire、addWaiter和unparkSuccessor等关键逻辑。

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

首先看到ReentrantLock构造方法

	//无参构造函数为非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    //可以传递一个boolean变量来选择使用公平锁或非公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

lock方法

可中断的lock,和可超时lock,和lock类似。能看懂lock方法,一定可以看懂另外两个,这里限于篇幅就不讲了。

	//FairSync(公平锁),直接调用AbstractQueuedSynchronizer(AQS)的acquire方法。
	final void lock() {
        acquire(1);
    }
    //NonfairSync(非公平锁)
    final void lock() {
    	//cas将aqs的state变量从0修改为1(尝试加锁)
        if (compareAndSetState(0, 1))
        	//将当前持有锁的线程设置为自己(加锁成功了),修改aqs的exclusiveOwnerThread属性为当前线程
            setExclusiveOwnerThread(Thread.currentThread());
        //cas失败(加锁失败)
        else
            acquire(1);
    }

看到aqs的acquire方法

    public final void acquire(int arg) {
    	//tryAcquire调用到子类自己的实现
    	//tryAcquire是去获取锁的具体实现,返回true代表加锁成功,返回false代表加锁失败
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

FairSync的tryAcquire方法

    protected final boolean tryAcquire(int acquires) {
    	//获取当前线程
        final Thread current = Thread.currentThread();
        //获取state变量
        int c = getState();
        //没有线程在持有锁
        if (c == 0) {
        	//hasQueuedPredecessors--->队列中是否有需要被唤醒的其他线程在排队等待
            if (!hasQueuedPredecessors() &&
            	//如果没有线程在排队,cas将state从0修改为acquires
                compareAndSetState(0, acquires)) {
                //cas修改state成功,将当前持有锁的线程设置为自己
                setExclusiveOwnerThread(current);
                //获取锁成功
                return true;
            }
        }
        //如果state!=0,则代表已经有线程持有了锁
        //判断持有锁的线程是否为自己,为自己则是重入加锁
        else if (current == getExclusiveOwnerThread()) {
        	//将当前state增加acquires
            int nextc = c + acquires;
            //int溢出,除非瞎搞,基本没可能
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            //设置state的值
            setState(nextc);
            //获取锁成功
            return true;
        }
        //cas失败,且不是重入,说明已经有其他线程持有锁,返回false,获取锁失败
        return false;
    }
    public final boolean hasQueuedPredecessors() {
    	//尾节点
        Node t = tail;
        //头节点
        Node h = head;
        Node s;
        //当没有锁竞争时,队列不会形成 head和tail均为null , 则 head == tail ,返回为false 
        //当队列中排队的线程,都已经被唤醒出队了,head和tail为同一个节点。返回为false 
        //h!=t,则队列中有至少两个节点,获取头结点的下个节点,如果下个节点为空(进来排队的线程将自己cas设置为tail,还没来得及将头结点的next属性设置为自己),则返回true
        //h!=t,则队列中有至少两个节点,获取头结点的下个节点,如果下个节点不为空,判断当前线程和头结点的下个节点是否为同一个线程,如果不是一个线程,则返回true。如果是一个线程,则代表就是自己被唤醒了,返回为false 
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

NonfairSync的tryAcquire方法

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        //没有线程在持有锁
        if (c == 0) {
        	//不会调用hasQueuedPredecessors判断是否有需要唤醒的线程在排队
        	//直接cas修改state的值,修改成功,则直接就获取到锁
            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;
    }    

接着看aqs的acquire的方法,先看到addWaiter方法

    public final void acquire(int arg) {
    	//如果tryAcquire返回false,则当前线程获取锁失败,执行后面的代码acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    private Node addWaiter(Node mode) {
    	//为当前线程创建一个node,其中node的nextWaiter属性为Node.EXCLUSIVE(就是null),thread属性为当前的前程。
        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的前一个节点设置为尾节点
            node.prev = pred;
            //cas将aqs的tail属性(尾节点)修改为node
            if (compareAndSetTail(pred, node)) {
            	//pred此时为倒数第二个节点(cas成功了,尾节点成了node)
            	//将倒数第二个节点的后一个节点设置为node
                pred.next = node;
                //返回node节点
                return node;
            }
        }
        //尾节点为空,队列未初始化。
        //或者上面的cas操作失败(有其他线程修改了尾节点)
        //则调用enq方法(入队)
        enq(node);
        //返回node
        return node;
    }
    private Node enq(final Node node) {
    	//死循环
        for (;;) {
        	//每次循环获取一次尾节点
            Node t = tail;
            //队列未初始化
            if (t == null) { // Must initialize
            	//cas 创建一个空的Node节点设置到aqs的head属性(头节点)
            	//cas失败,则有其他线程初始化了队列,接着下一次循环
                if (compareAndSetHead(new Node()))
                	//cas成功,设置尾节点 。 队列初始化完毕,但是node还未入队。接着下一次循环
                    tail = head;
            } 
            //队列已经初始化过
            else {
            	//将node的前一个节点指向尾节点
                node.prev = t;
                //cas将尾节点设置为node
                //cas失败则接着下一次循环,直至能够入队
                if (compareAndSetTail(t, node)) {
                	//cas成功 
                	//t 此时为倒数第二个节点
            	    //将倒数第二个节点的后一个节点指向node(尾节点)
                    t.next = node;
                    //跳出循环
                    return t;
                }
            }
        }
    }

再看到acquireQueued方法,此时node已经入队了。

	final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //死循环
            for (;;) {
            	//获取node的前一个节点
                final Node p = node.predecessor();
                //如果前一个节点是头结点,执行tryAcquire去获取锁
                if (p == head && tryAcquire(arg)) {
                	//获取锁成功,将node设置为头结点,将node的thread属性和prev属性置为空(next属性还在哦如果有后继节点的话)
                    setHead(node);
                    //将之前的头结点的next属性置为空
                    p.next = null; // help GC
                    //只要被唤醒抢到锁,就会修改failed为false,没抢到锁则接着休眠
                    failed = false;
                    //返回true的话,则说明被打断过,则会调用后面的selfInterrupt()方法。
                    //selfInterrupt()---> Thread.currentThread().interrupt(); 重新中断一次,设置中断标识,来标识曾经被中断过。
                    return interrupted;
                }
                //shouldParkAfterFailedAcquire返回false,则进行下一次循环。
                //shouldParkAfterFailedAcquire返回为true,调用parkAndCheckInterrupt方法休眠。
                //当node被唤醒时,从这里开始继续循环去tryAcquire
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //lock方法不能被打断,lockInterruptibly方法才可以,打断后在这里抛出打断异常,由于发生异常,跳出循环并可以使使用者捕获该异常做处理。
                    //lock方法如果被打断过,parkAndCheckInterrupt会清空打断标识(可以再次休眠)并返回true,然后将interrupted置为true,再次进行下一次循环
                    interrupted = true;
            }
        } finally {
            if (failed)
            	//failed没有改成false,比如lockInterruptibly方法抛出中断异常,该节点就被取消了。
                cancelAcquire(node);
        }
    }
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    	//获取前一个节点的waitStatus属性。
        int ws = pred.waitStatus;
        //如果为SIGNAL(-1),直接返回true
        //第二次再进来,前一个节点已经是SIGNAL,直接返回true
        if (ws == Node.SIGNAL) 
            return true;
        //如果大于0,只能是CANCELLED状态,表示前一个节点是被取消的状态,该节点不再会去使用和唤醒
        if (ws > 0) {
        	//则一直向前遍历,直到找到waitStatus不大于0的节点(一定可以找到,头结点不会大于0)
        	//并将node的上一个节点设置为该节点
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            //找到waitStatus不大于0的节点,将该节点下一个节点设置为node,忽略中间所有的CANCELLED节点
            pred.next = node;
        } else { 
        	//cas将上一个节点的waitStatus设置为SIGNAL(-1),表示该节点需要负责唤醒下一个节点
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        //第一次进来,将前一个节点设置为SIGNAL后,返回false
        return false;
    }
    private final boolean parkAndCheckInterrupt() {
    	//在此休眠
        LockSupport.park(this);
        //返回打断标识,并清空打断标识
        return Thread.interrupted();
    }
    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

看到cancelAcquire方法,取消队列中的节点

    private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;
		
		//将节点的thread置为空
        node.thread = null;

        //获取上一个节点
        Node pred = node.prev;
        //如果上一个节点也是CANCELLED状态,则一直向前遍历,直到找到waitStatus不大于0的节点
        //并将node的上一个节点设置为该节点
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        //获取上一个节点的后继节点,可能不是node,因为上面发生了跳过,也就是predNext 可能是一个CANCELLED状态的节点
        Node predNext = pred.next;
		
		//将节点的状态设置为CANCELLED
        node.waitStatus = Node.CANCELLED;
		
        //如果node是尾节点,则cas将尾节点设置成找到的上一个waitStatus不大于0的节点
        if (node == tail && compareAndSetTail(node, pred)) {
        	//cas设置成功,则pred成为了尾节点,cas将pred的后继节点置为空
            compareAndSetNext(pred, predNext, null);
        } 
        //如果node不是尾节点,或者node是尾节点,但是cas设置尾节点失败
        else {  
            int ws;
            //如果pred不是头节点
            //且(( pred的waitStatus为SIGNAL(-1)(需要去唤醒后继节点) 或者 (pred的waitStatus小于等于0,则cas设置pred的waitStatus为SIGNAL(-1) )))
            //且pred的thread属性不为空
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                //获取node的后继节点
                Node next = node.next;
                //如果后继节点不为空,且waitStatus <= 0 (不是CANCELLED状态)
                if (next != null && next.waitStatus <= 0)
                	//将pred的后继节点设置为next(由于原本next要由node唤醒,但是node被取消了,唤醒职责交由pred)
                    compareAndSetNext(pred, predNext, next);
            } 
            //否则 
            //本来应该唤醒的是node,但是node被取消了,则直接唤醒node的后面的节点(如果有可以唤醒的节点的话)
            else {
                unparkSuccessor(node);
            }
			
			//将node的后继节点设置为自己
            node.next = node; // help GC
        }
    }

unlock方法

    public void unlock() {
        sync.release(1);
    }
    public final boolean release(int arg) {
    	//如果state扣减为0
        if (tryRelease(arg)) {
        	//获取头节点
            Node h = head; 
            if (h != null && h.waitStatus != 0)
            	//唤醒后面的节点
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    protected final boolean tryRelease(int releases) {
    	//获取state减去releases的值
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        //如果为0
        if (c == 0) {
            free = true;
            //将当前持有锁的线程设置为空
            setExclusiveOwnerThread(null);
        }
        //将扣减后的值赋值回state
        setState(c);
        //可能不为0,返回false,比如重入
        return free;
    }
    private void unparkSuccessor(Node node) {
    	//获取节点的waitStatus
        int ws = node.waitStatus;
        //如果小于0,比如SIGNAL(-1)
        if (ws < 0)
        	//cas将节点的waitStatus改为0
            compareAndSetWaitStatus(node, ws, 0);
        
        //获取node的下一个节点    
        Node s = node.next;
        //如果没有下一个节点
        //或者下一个节点waitStatus 大于0 (被取消了)
        if (s == null || s.waitStatus > 0) {
            s = null;
           	//从队列尾部往前开始找,直到找到的前一个节点为空或者找到了node自己
            for (Node t = tail; t != null && t != node; t = t.prev)
            	//找到的节点的waitStatus小于等于0
                if (t.waitStatus <= 0)
                	//将找到的节点赋值给s
                    s = t;
        }
        //如果有可以唤醒的节点,则唤醒他
        if (s != null)
            LockSupport.unpark(s.thread);
    }

到此,ReentrantLock的lock和unlock方法就解析完毕了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值