ReentrantLockロックを手書きで


手書きReentrantLock
最近Java言語のロックに関する知識を学び、ReentrantLockソースコードを見て、自分でReentrantLockを手書きしました.ReentrantLockは再入可能なロックであり、ソースコードにおいて構造関数によってフェアロックと非フェアロックとの間で変換することができる.
再ロック可能、すなわち、現在のスレッドは、ロックを解除せずに複数回ロックを取得できますが、ロックを解除する回数は、ロックを取得する回数と同じでなければなりません.そうしないと、IllegalMonitorStateExceptionの異常が放出されます.
フェアロックと非フェアロックの違いは、フェアロックが待機キューに先に入ったスレッドに先にロックを取得させ、各スレッドがロックを取得する機会を与えることです.非フェアロックは、各スレッドがロックを取得する確率が不確定であり、非フェアロックの同時性は良好であるが、一部のスレッドが長時間ロックを取得できない可能性がある.
ReentrantLockに再入力不可、不公平
/**
 *      
 */
public class WonderReentranrLock{
    //        ,            
    private AtomicReference<Thread> owner = new AtomicReference<>();
    //           
    private Queue<Thread> waitQueue = new LinkedBlockingQueue();

    /**
     *   
     */
    public void lock(){
        //    
        if(!tryLock()){
            Thread current = Thread.currentThread();
            //        
            waitQueue.offer(current);
            //      
            for(;;){
                //      
                Thread head = waitQueue.peek();
                //           
                if(current == head){
                    //  
                    if(!tryLock()){
                        //      
                        LockSupport.park();
                    }else{
                        //      ,     
                        waitQueue.poll();
                        return;
                    }
                }else{
                    //        ,     
                    LockSupport.park();
                }
            }
        }
        //       
    }

    /**
     *     
     * @return
     */
    public boolean tryLock(){
        Thread current = Thread.currentThread();
        //CAS         ,   
        return owner.compareAndSet(null,current);
    }

    /**
     *   
     */
    public void unLock(){
        //         
        if(tryUnLock()){
            Thread head = waitQueue.peek();
            //        
            if(head!=null){
                //              
                LockSupport.unpark(head);
            }
        }
    }

    /**
     *     
     * @return
     */
    public boolean tryUnLock(){
        Thread current = Thread.currentThread();
        //           
        if(owner.get()!=current){
            throw new IllegalMonitorStateException();
        }else{
            //   CAS     
            return owner.compareAndSet(current,null);
        }
    }
}

このReentrantLockは不公平で、再入できない鍵です.
ここに穴があります:ロックをかける時にキューの頭があるかどうかを判断したが、キューの頭であればロックを奪わせ、頭でなければ掛けさせ、ここはキューの先進的な先出のように見え、公平なロックのようですが、マルチスレッドではキューの頭部とキューにいないスレッドのロックが発生する可能性があり、頭スレッドはロックに失敗する可能性があります.だから非公平です.
再入可能なロック(非公平)をもう一つ書きますが、公平なロックは実現が複雑なようで、まず考えません.
ReentrantLockに再入力可能、不公平
/**
 *     
 */
public class WonderReentrantLock2 {
    //        ,    owner      
    private Thread owner = null;
    //      ,  ,   count
    private AtomicInteger count = new AtomicInteger();
    //           
    private Queue<Thread> waitQueue = new LinkedBlockingQueue();

    /**
     *   ,        
     */
    public void lock(){
        //    
        if(!tryLock()){
            Thread current = Thread.currentThread();
            //        
            waitQueue.offer(current);
            //      
            for(;;){
                //      
                Thread head = waitQueue.peek();
                //           
                if(current == head){
                    //  
                    if(!tryLock()){
                        //      
                        LockSupport.park();
                    }else{
                        //      ,     
                        waitQueue.poll();
                        return;
                    }
                }else{
                    //        ,     
                    LockSupport.park();
                }
            }
        }
        //       
    }

    /**
     *     
     * @return
     */
    public boolean tryLock(){
        Thread current = Thread.currentThread();
        //      
        int ct = count.get();
        //    0,           
        if (ct!=0){
            //             
            if(owner==current){
                //  ,  +1
                count.set(ct+1);
                return true;
            }else{
                //      ,    
                return false;
            }
        }else{
            //   0,          ,CAS    
            if(count.compareAndSet(ct,ct+1)){
                //     owner       
                owner = current;
                return true;
            }else{
                //CAS    
                return false;
            }
        }
    }

    /**
     *   
     */
    public void unLock(){
        //         
        if(tryUnLock()){
            Thread head = waitQueue.peek();
            //        
            if(head!=null){
                //              
                LockSupport.unpark(head);
            }
        }
    }

    /**
     *     
     * @return
     */
    public boolean tryUnLock(){
        Thread current = Thread.currentThread();
        //           
        if(owner!=current){
            throw new IllegalMonitorStateException();
        }else{
            //      
            int ct = count.get();
            //   count  next  
            int next = ct - 1;
            //count.set(next);   !
            if(next==0){
                //   count 0  ,         , owner  null
                owner = null;
                //owner null   count    0
                count.set(next);
                return true;
            }else{
                //  next  0,           ,  count  ,owner  
                count.set(next);
                return false;
            }
        }
    }
}

再入可能ロックと再入不可ロックの違いは、再入不可ロックはスレッドがowner(スレッドタグ)を争う過程であり、再入可能ロックはcountがスレッドの再入を記録する回数を増加させることである.従って、再入ロックは、フラッシュロック時に、まずcountが0であるか否かを判断し、countが0であるとロックが空き、CASを用いてcountを修正し、成功後にownerを現在のスレッドに修正し、フラッシュロックに成功する.countが0でない場合、ロックがあるスレッドによって占有されていることを示すと、占有ロックのスレッドが現在のスレッドであるか否かを判断し、もしそうであればcountに1を加えると再入が実現され、現在のスレッドでなければロックは失敗して停止する.
書き込み再ロック時にピットが発生し、コード内のtryUnLock()メソッドownerが現在のスレッドである場合、ロック解除が開始され、最初はcount値を元の値に変更して1を減らし、ownerをnullに設定します.実行後、IllegalMonitorStateExceptionエラーが報告されていることがわかりました.つまり、現在のスレッドがロックされた後、ロックを解除しようとするとownerは現在のスレッドではありません.これは、countを変更してからownerを変更し、変更後のcount値が0の場合、他のスレッドがロックを奪い、ownerを変更するため、ownerは元のスレッドではありません.ここでcountを変更してowner=nullの後に置くと問題を解決できます.ownerをAtomicReferenceタイプに変更して解決することもできます.
テストコード
public class Test {
    static WonderReentrantLock lock = new WonderReentrantLock();
    static WonderReentrantLock2 lock2 = new WonderReentrantLock2();

    volatile static int count=0;

    public static void main(String[] args) throws InterruptedException {
        for(int i=0; i<10; i++){
            new Thread(){
                @Override
                public void run() {
                    for(int j=0; j<10000; j++){
                        lock2.lock();//  
                        count++;
                    }
                    for(int j=0; j<10000; j++){
                        lock2.unLock();//     ,     
                    }
                    System.out.println("over...");
                }
            }.start();
        }
        Thread.sleep(4000);
        System.out.println(count);

        /*for(int i=0; i<10; i++){
            new Thread(){
                @Override
                public void run() {
                    for(int j=0; j<10000; j++){
                        lock.lock();//    
                        count++;
                        lock.unLock();
                    }
                    System.out.println("over...");
                }
            }.start();
        }
        Thread.sleep(4000);
        System.out.println(count);*/
    }
}