QA MichaelPeng

一个QA的零零碎碎

ReentrantLock代码剖析之ReentrantLock.unlock

下面来看ReentrantLock.unlock,尝试在当前锁的锁定计数即state值上减1,而lock每次把锁定计数加1,这也是为什么lock和unlock必须成对出现,否则锁定计数就不能正常恢复到0,其它线程就不能尝试获取锁

复制代码
ReentrantLock.unlock()
/**
     * Attempts to release this lock.
     *
     * <p>If the current thread is the holder of this lock then the hold
     * count is decremented.  If the hold count is now zero then the lock
     * is released.  If the current thread is not the holder of this
     * lock then {
@link IllegalMonitorStateException} is thrown.
     *
     * 
@throws IllegalMonitorStateException if the current thread does not
     *         hold this lock
     
*/
    
public void unlock() {
        sync.release(
1);
    }
复制代码


AbstractQueuedsynchronizer.release(int arg)方法会在锁定数目上减去arg,若新锁定数目为0,表示锁被当前线程释放, 则试图唤醒等待队列中的下一个线程。请注意这里仅是唤醒,而非把锁的所有权交给下一个线程。该线程能否成功获取锁,还要看运气。

 

复制代码
AbstractQueuedsynchronizer.release(int arg)
/**
     * Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {
@link #tryRelease} returns true.
     * This method can be used to implement method {
@link Lock#unlock}.
     *
     * 
@param arg the release argument.  This value is conveyed to
     *        {
@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * 
@return the value returned from {@link #tryRelease}
     
*/
    
public final boolean release(int arg) {
        
if (tryRelease(arg)) {
            Node h 
= head;
            
/*head的waitStatus不为零表示它的后继在等待唤醒,
             还记得AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire中对waitStatus的操作么?waitStatus!=0表明或者处于CANCEL状态,或者是置SIGNAL表示下一个线程在等待其唤醒,CANCEL状态这里先不分析,可以认为这里!=0即表示SIGNAL状态
*/
            
if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            
return true;
        }
        
return false;
    }
复制代码


tryRelease定义在Sync类中

Sync.tryRelease会在当前锁定状态上减去要释放的锁定数,如果锁定状态为零则置当前持有锁线程为null,返回true,否则返回false

此外还会检查是否是占有锁的线程在调用,若不是则抛出IllegalMonitorStateException。

 

复制代码
Sync.tryRelease(int releases)
  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;
        }
复制代码


再来看unparkSuccessor

 

复制代码
AbstractQueuedSynchronizer.unparkSuccessor(Node node)
    /**
     * Wakes up node's successor, if one exists.
     *
     * 
@param node the node
     
*/
    
private void unparkSuccessor(Node node) {
        
/*
         * Try to clear status in anticipation of signalling.  It is
         * OK if this fails or if status is changed by waiting thread.
         
*/
//若当前节点waitStatus被置为SIGNAL则清零
        compareAndSetWaitStatus(node, Node.SIGNAL, 0);

        
/*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         
*/
        
// 若后续节点为空或已被cancel,则从尾部开始找到队列中第一个waitStatus<=0,即未被cancel的节点
        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);
    }
复制代码

 

再回头看AbstractQueuedSynchronizer.acquireQueued,注释1,2,3,4表明了在线程被唤醒后的执行顺序

复制代码
AbstractQueuedSynchronizer.acquireQueued(final Node node, int arg)
/**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * 
@param node the node
     * 
@param arg the acquire argument
     * 
@return {@code true} if interrupted while waiting
     
*/
    
final boolean acquireQueued(final Node node, int arg) {
        
try {
            
boolean interrupted = false;
            
for (;;) {
                
//2 因为等待队列只会从尾部插入,所以在被唤醒节点和头节点之间不会新加入等待节点,只会有节点被cancel
                
//3 如果被唤醒节点不是头节点的直接后续,由唤醒算法可以保证其和头结点之间没有waitStatus<=0的节点,但有可能有waitStatus大于0,即被CANCEL的节点,参见注释4
                 
// 5获取锁成功则把当前节点设置为头节点,返回,否则线程继续被禁止,直到下一次被唤醒
                final Node p = node.predecessor();
                
if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next 
= null// help GC
                    return interrupted;
                }
                
// 1 被lock.release唤醒,parkAndCheckInterrupt返回false,继续回到for循环开始
                  
//4 被cancel的结点在shouldParkAfterFailedAcquire中会被清理掉,当前节点会成为头节点的直接后继,然后return false,在下次循环中即可尝试获取锁
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted 
= true;
            }
        } 
catch (RuntimeException ex) {
            cancelAcquire(node);
            
throw ex;
        }
    }

复制代码

 

 

posted on   Michael Peng  阅读(2543)  评论(0编辑  收藏  举报

编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
< 2010年2月 >
31 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 1 2 3 4 5 6
7 8 9 10 11 12 13

导航

统计

点击右上角即可分享
微信分享提示