天天看點

分析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