手擼JDK之ReentrantLock鎖那點事

2020-09-22 11:01:03

前置思想

首先明確一點ReentrantLock與synchronized最大的不同點在於synchronized是鎖住了物件的頭部,而ReentrantLock是類似於我們買一把鎖,鎖住我們不想因為多執行緒下導致資源的原子性 ,可見性的問題,在明確這一點時我們的鎖其實與生活中的所沒太大區別,生活中的鎖無非就是大門掛一把鎖,這樣我們就告訴別人此處以上鎖,唯一不同的是程式鎖必須由上鎖的人來解鎖,而生活中我們可以持有別人的鎖進行開鎖。
在這裡插入圖片描述

主要使用到的類

  • AtomicInteger:主要保證此鎖的原子性與可見性,以及涉及到後面的可重入鎖的狀態,注:volatile只能保證資源的可見性,並不能保證原子性
  • Thread:當前那個執行緒持有這把鎖【PS:也就是誰持有了鎖,此處到後面可重入鎖,以及解鎖所需要的】
  • LinkedBlockingQueue:沒搶到鎖物件的容器
  • LockSupport:切換執行緒狀態

交代完了開懟!!!!

/**
     * 執行緒狀態定義
     */
    AtomicInteger state = new AtomicInteger();

    /**
     * 那個執行緒持有鎖
     */
    Thread ownerThread = null;

    /**
     * 存放執行緒的容器
     */
    LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();

這步沒啥可說的繼續!!

public boolean tryLock() {
        //不用每次都進行CAS操作,如果當期狀態為0我直接返回搶鎖成功
        if (state.get() == 0) {
            //CAS搶鎖
            if (state.compareAndSet(0, 1)) {
                //將搶到鎖的執行緒設定為當前鎖的持有者
                ownerThread = Thread.currentThread();
                return true;
            }
        } else if (ownerThread == Thread.currentThread()) {
            System.out.println("重入鎖成功!!!!");
            state.set(state.get() + 1);
            return true;
        }
        return false;
    }

此處主要用到了CAS機制,如果修改成功證明已搶到鎖,失敗則加入佇列,以及重入鎖如果是當前執行緒那麼獲取鎖成功,並且在狀態+1【PS:此處狀態+1主要是為了可重入鎖,應為重入鎖的本質需要你加鎖幾次就要釋放幾次】

public void lock() {
        //真公平鎖  如果沒有!waiters.isEmpty(),在鎖的上一個持有者他也會搶
        if (!waiters.isEmpty() || !tryLock()) {
            //沒搶到鎖進入等待佇列
            waiters.add(Thread.currentThread());
            for (; ; ) {
                if (tryLock()) {
                    //移除並且返回佇列頭部
                    waiters.poll();
                    return;
                } else {
                    //沒搶到   進入阻塞(WAITING)
                    LockSupport.park();
                }
            }
        }
    }

如果當前沒有執行緒持有鎖則直接返回,有則將他加入佇列並且阻塞【PS:當然一些猿認為使用LinkedBlockingQueue不就已經是公平鎖了麼(先入先出),其實不然比如:有三個執行緒,1搶鎖成功,在釋放後其實1也參與搶鎖了,也就是插隊!!此處的插隊就導致看似公平的鎖變的不公平了】

public void unlock() {
        //如果不是當前執行緒那麼不允許釋放鎖
        if (Thread.currentThread() != ownerThread) {
            throw new RuntimeException("不是你的別亂動,你不是鎖的持有者");
        }
        //釋放鎖的過程,每次過來減一
        if (state.decrementAndGet() == 0) {
            ownerThread = null;
            //獲取第一個等待的元素並且通知他開始搶鎖了,此時還不能移除
            //在真正搶到鎖後才能移除佇列
            Thread waiter = waiters.peek();
            if (waiter != null) {
                LockSupport.unpark(waiter);
            }
        }
    }

此處我們釋放鎖的時候必須是當前持有鎖的執行緒,如果其他執行緒來釋放鎖呢就亂套了
PS:如有不對的地方請大手不吝嗇指出