一篇文章,讓你明白重入鎖,自旋鎖,公平鎖及非公平鎖

公平鎖,就是很公平,在併發環境中,每個線程在獲取鎖時會先查看此鎖維護的等待隊列,如果為空,或者當前線程線程是等待隊列的第一個,就佔有鎖,否則就會加入到等待隊列中,以後會按照FIFO的規則從隊列中取到自己非公平鎖比較粗魯,上來就直接嘗試佔有鎖,如果嘗試失敗,就再採用類似公平鎖那種方式

白話文:就是公平鎖是先到先得,按序進行. 非公平鎖就是不排隊直接拿,失敗再說

//在Java中是通過構造指定鎖的類型的//true:公平鎖//false:非公平鎖public ReentrantLock(boolean fair) {  sync = fair ? new FairSync() : new NonfairSync();  }

前言

重入鎖(ReentrantLock)是一種遞歸無阻塞的同步機制。重入鎖,也叫做遞歸鎖,指的是同一線程 外層函數獲得鎖之後 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。在JAVA環境下 ReentrantLock 和synchronized 都是 可重入鎖

Advertisements

例子

lock例子

public class Lock{  private boolean isLocked = false;  public synchronized void lock() throws InterruptedException{  while(isLocked){  wait();  }  isLocked = true;  }  public synchronized void unlock(){  isLocked = false;  notify();  } }

重入鎖不是只判斷isLocked,而是還要獲取下當前的Thread.currentThread()判斷是否是已經加鎖了.

Advertisements

Thread thread = Thread.currentThread();while(isLocked && lockedBy != thread){  wait(); }

重入鎖,即判斷是否鎖,是否為當前線程,如果是當前線程的話,才wait()

如果是其他線程,直接就wait

自旋鎖

由於自旋鎖使用者一般保持鎖時間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠高於互斥鎖。

如何旋轉呢?何為自旋鎖,就是如果發現鎖定了,不是睡眠等待,而是採用讓當前線程不停地的在循環體內執行實現的,當循環的條件被其他線程改變時 才能進入臨界區

代碼演示如何自選

/** * @Package: com.example.lock * @Description: 自旋鎖 * @author: liuxin * @date: 2017/8/28 下午4:48 */public class SpinLock { //初始化為當前線程 private AtomicReference<Thread> sign = new AtomicReference<>();  public void lock() {  Thread current = Thread.currentThread();  //null 不等於當前線程,返回false !fasle=true進入自選  while (!sign.compareAndSet(null, current)) { } }  public void unlock() {  Thread current = Thread.currentThread(); //對比current= 初始化信息,所以為true,並設置為null,此時// while (!sign.compareAndSet(null, current)),所以,null=null,lock中自旋結束,當一個鎖完成,sign中有回到初始化狀態。 sign.compareAndSet(current, null); }}

使用了CAS原子操作,lock函數將owner設置為當前線程,並且預測原來的值為空。unlock函數將owner設置為null,並且預測值為當前線程。

當有第二個線程調用lock操作時由於owner值不為空,導致循環一直被執行,直至第一個線程調用unlock函數將owner設置為null,第二個線程才能進入臨界區。

由於自旋鎖只是將當前線程不停地執行循環體,不進行線程狀態的改變,所以響應速度更快。但當線程數不停增加時,性能下降明顯,因為每個線程都需要執行,佔用CPU時間。如果線程競爭不激烈,並且保持鎖的時間段。適合使用自旋鎖。

Lock常用API

MethodExplain
lock.getHoldCount()查詢當前線程報錯此鎖定的個數,也就是調用lock()的次數
lock.getQueueLength()返回正等待獲取此鎖定的線程估計數,比如有5個線程,1個線程首先執行await()方法,那麼在調用getQueueLength()方法后返回值是4,說明有4個線程同時在等待lock的釋放。
lock.getWaitQueueLength(condition)返回等待與此鎖定相關的給定條件Condition的線程估計數,比如有5個線程,每個線程都執行了同一個condition對象的await()方法,則調用getWaitQueueLength(Conditioncondition)方法時返回的int值是5。
lock.hasQueuedThread(thread)查詢指定的線程是否正在等待獲取此鎖定
lock.hasQueuedThreads()查詢是否有線程正在等待此鎖定
lock.hasWaiters(condition)是查詢是否有線程正在等待與此鎖定有關的condition條件
lock.isHeldByCurrentThread()查詢當前線程是否保持此鎖定
lock.isLocked()查詢此鎖定是否由任意線程保持
lock.lockInterruptibly()如果當前線程未被中斷,則獲取鎖定,如果已經被中斷則出現異常
lock.tryLock()僅在調用時鎖定未被另一個線程保持的情況下,才獲取該鎖定
lock.tryLock(long timeout, TimeUnit unit)方法booleantryLock(longtimeout,TimeUnitunit)的作用是,如果鎖定在給定等待時間內沒有被另一個線程保持,且當前線程未被中斷,則獲取該鎖定

Advertisements

你可能會喜歡