天天看点

分析ReentrantLock之unlock

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

分析ReentrantLock之unlock

     public   void  unlock() {

        sync.release( 1 );

    }

分析ReentrantLock之unlock

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

分析ReentrantLock之unlock

     public   final   boolean  release( int  arg) {

         if  (tryRelease(arg)) {

            Node h  =  head;

             if  (h  !=   null   &&  h.waitStatus  !=   0 )

                unparkSuccessor(h);

             return   true ;

        }

         return   false ;

    }

分析ReentrantLock之unlock

tryRelease定义在Sync类中

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

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

分析ReentrantLock之unlock

   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;

        }

分析ReentrantLock之unlock

再来看unparkSuccessor

分析ReentrantLock之unlock

     private   void  unparkSuccessor(Node node) {

// 若当前节点waitStatus被置为SIGNAL则清零

        compareAndSetWaitStatus(node, Node.SIGNAL,  0 );

         //  若后续节点为空或已被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);

    }

分析ReentrantLock之unlock

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

分析ReentrantLock之unlock

     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;

        }

    }

分析ReentrantLock之unlock