Lock接口與AQS隊列同步器

Lock接口

首先要說明的就是Lock,通過查看Lock的源碼可知,Lock是一個接口:

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}
  • Lock():獲取鎖
  • lockInterruptibly():可中斷地獲取鎖,即該方法會響應中斷,在鎖的獲取中可以中斷當前線程。
  • tryLock():嘗試獲取鎖,調用該方法會立即返回,如果能夠獲取則返回true 否則返回true
  • tryLock(long time, TimeUnit unit):超時嘗試獲取鎖,當前線程在超時時間內獲得了鎖返回true,當前線程在超時時間內被中斷返回false,超時時間結束返回false。
  • unlock():釋放鎖
  • newCondition():後面再說咯 還沒看到=。=

個人的理解:Lock就是一個方法的標準規範,後面如果我們需要實現我們自己的寫的同步器的話,需要實現該接口,統一調用的方法。還有就是Lock與synchronized之間的不同之處:

1、synchronized是隱形的獲取和釋放鎖,而lock則需要我們手動的獲取和釋放。

2、Lock可以讓等待鎖的線程響應中斷,線程可以中斷去幹別的事務,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不能夠響應中斷

3、Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現,在代碼執行時出現異常,JVM會自動釋放鎖定。lock是通過代碼實現的,要保證鎖定一定會被釋放,就必須將 unLock()放到finally{} 中;

4、通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。

5、Lock可以提高多個線程進行讀操作的效率。例如:當有多個線程讀寫文件時,讀操作和寫操作會發生衝突現象,寫操作和寫操作會發生衝突現象,但是讀操作和讀操作不會發生衝突現象。

但是採用synchronized關鍵字來實現同步的話,就會導致一個問題:

如果多個線程都只是進行讀操作,所以當一個線程在進行讀操作時,其他線程只能等待無法進行讀操作。

因此就需要一種機制來使得多個線程都只是進行讀操作時,線程之間不會發生衝突,通過Lock(子類)就可以辦到。

AQS隊列同步器

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
      private volatile int state;

}

是用來構建鎖或者其他同步組件的基礎框架,使用一個int成員變量state來表示同步狀態,通過內置的FIFO隊列來完成資源獲取線程的排隊工作。

同步器的主要使用方式是繼承,子類通過繼承同步器並實現他的抽象方法來管理同步狀態。

同步器中主要有3類方法:

1、模版方法----用於調用我們實現的抽象方法,來實現具體的語義。

2、同步器提供對同步狀態進行修改的基礎方法。

3、需要實現的抽象方法----關乎於我們所實現的同步器的。

下面根據源碼來一個個分析

模版方法

主要含有:

acquire(int arg): 獨佔式獲取同步狀態,如果當前線程獲取同步狀態成功,則由該方法返回,否則,將會進入同步隊列等待,該方法將會調用重寫的tryAcquire(int arg)。

acquireInterruptibly(int arg): 與acquire相同,但是該方法可以響應中斷,當前線程未獲取到同步狀態而進入同步隊列,如果當前線程被中斷,則該方法會拋出InterruptedException並返回

boolean tryAcquireNanos(int arg,long naps): 在acquireInterruptibly(int arg)的基礎上增加了超時限制。

acquireShared(int arg): 共享式的獲取同步狀態,如果當前線程未獲取到同步狀態,將會進入同步隊列等待,與獨佔式獲取的主要區別是在同一時刻可以有多個線程獲取到同步狀態。

acquireSharedInterruptibly(int arg): 與acquireShared相同,響應中斷

boolean tryAcquireSharedNaos(int arg,long nanos): acquireSharedInterruptibly(int arg)的基礎上增加了超時限制。

boolean release(int arg): 獨佔式釋放同步狀態,該方法會在釋放同步狀態之後,將同步隊列中的一個節點包含的線程喚醒。

boolean releaseShared(int arg): 共享式釋放同步狀態

Collection getQueuedThreads(): 獲取等待在同步隊列上的線程集合

舉例子:

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

public final boolean release(int arg) {
  //釋放鎖成功後
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
              //喚醒頭節點後面的線程來進行GGGGGGGG
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

上面分別的給出了其中的兩個模版方法。同步器本身是採用了模版方法的設計模式。

何爲模版方法設計模式???

就是通過一個抽象類定義好了模版方法,模版方法中通過組合不同的抽象方法來實現具體語義,而抽象方法的具體實現由用戶實現的子類來實現,即模版方法定義好了調用那些抽象方法來實現一個功能,而底層的實現由抽象方法來實現。爲了保證模版方法的不可改變 都採用了final進行修飾

可以看出來acquire方法內部調用了tryAcquire() 這個tryAcquire就是我們需要實現的抽象方法。release方法內部也是調用了tryRelease()---->抽象方法

同步器提供對同步狀態進行修改的基礎方法

主要含有:

getState(): 獲取當前同步狀態。

setState(int newState): 設置當前同步狀態。

compareAndSetState(int expect,int update): 使用CAS來設置當前狀態,該方法能夠保證狀態設置的原子性。

舉例子:

protected final int getState() {
        return state;
}

上面提出了其中一個getstate方法,後面我們繼承同步器所重寫的抽象方法就需要依據這幾個同步器提供的方法來實現我們需要實現的語義。

這些雖然由final修飾,但是並不屬於模版方法,因爲其沒有調用需要我們重寫的模版方法。so 這頂多算是同步器已經實現好的一些基礎方法。

需要實現的抽象方法

主要含有:

boolean tryAcquire(int arg): 獨佔式獲取共享狀態,實現該方法需要查詢當前狀態(getState)並判斷同步狀態是否符合預期,然後再進行CAS設置同步狀態

boolean tryRelease(int arg): 獨佔式釋放同步狀態,等待獲取同步狀態的線程將有機會獲取同步狀態。

int tryAcquireShared(int arg): 共享式獲取同步狀態,返回大於等於0的值,表示獲取成功,反之,獲取失敗。

boolean tryReleaseShared(int arg): 共享式釋放同步狀態

Boolean isHeldExclusively(): 當前同步器是否在獨佔模式下被線程佔用,一般該方法表示是否被當前線程所佔。

//AQS中的抽象tryRelease方法
  protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
 }

//可重入鎖ReentrantLock的內部類Sync實現了AQS實現的tryRelease方法
public class ReentrantLock implements Lock, java.io.Serializable {
  private final Sync sync;
	abstract static class Sync extends AbstractQueuedSynchronizer {
          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可重入鎖這個同步組件,通過持有一個內部類,這個內部類繼承了AbstractQueuedSynchronizer實現了一個同步器,並且按照自己同步器的需求實現了抽象方法。(sync下還有FairSync or NonfairSync 公平鎖以及非公平鎖)。

總結:

Lock接口就是一個用於規範調用方法的接口,方便了用戶。,AQS同步器就是一個爲了方便我們自己實現同步組件的,實現一個同步組件的過程:建立一個同步組件實現lock接口,規範好標準的方法,然後實現一個內部同步器累繼承與AQS,實現其抽象方法。提供模版方法給lock接口中的標準方法調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章