JUC鎖框架——重入鎖ReentrantLock

重入鎖(ReentrantLock)

  ReentrantLock是一種可重入的互斥鎖,而且加鎖是一種顯式操做,對邏輯控制的靈活性遠遠大於synchronized關鍵字。重入鎖是能夠徹底替代synchronized。而且重入鎖的性能是遠高於synchronized的,可是jdk6.0開始,jdk對synchronized作了大量的優化,使得二者性能差距不大。另外,ReentrantLock可結合Condition、以及提供了中斷響應、鎖申請等待限時、公平鎖等。java

公平鎖、非公平鎖(ReentrantLock)

  ReentrantLock分爲「公平鎖」和「非公平鎖」。它們的區別體如今獲取鎖的機制上是否公平。在「公平鎖」的機制下,線程依次排隊獲取鎖;而「非公平鎖」在鎖是可獲取狀態時,無論本身是否是在隊列的開頭都會獲取鎖。函數

 運行以下代碼,對比公平鎖和非公平鎖的運行結果性能

public static void main(String[] args) throws InterruptedException {
        Lock noFairReentrantLock = new ReentrantLock();
        Lock fairReentrantLock = new ReentrantLock(true);

        Thread[] threads = new Thread[10];
        for(int i=0;i<10;i++){
            threads[i] = new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"  [start]");
                fairReentrantLock.lock();//也能夠切換爲非公平鎖,觀察運行結果
                try {
                    System.out.println(Thread.currentThread().getName()+"  [得到鎖]");
                }finally {
                    fairReentrantLock.unlock();
                }
            });
        }
        for(int i=0;i<5;i++){
            threads[i].start();
        }
        for(int i=0;i<5;i++){
            threads[i].join();
        }
    }

}

重入鎖的中斷響應功能

對於synchronized塊來講,要麼獲取到鎖執行,要麼持續等待。而重入鎖的中斷響應功能就合理地避免了這樣的狀況。好比,一個正在等待獲取鎖的線程被「告知」無須繼續等待下去,就能夠中止工做了。優化

下面咱們看一個利用中斷響應功能解決的一個死鎖問題線程

public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock1 = new ReentrantLock();
        ReentrantLock lock2 = new ReentrantLock();

        //線程t1和t2構成了死鎖,此時咱們能夠中斷的方式解決死鎖問題
        Runnable runnable = ()->{
            try {
                if(Thread.currentThread().getName().equals("t1")){
                    lock1.lockInterruptibly(); //在加鎖的過程當中仍然能夠相應中斷
                    Thread.sleep(100);
                    lock2.lockInterruptibly();
                }else{
                    lock2.lockInterruptibly();
                    Thread.sleep(100);
                    lock1.lockInterruptibly();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                if (lock1.isHeldByCurrentThread()) lock1.unlock();
                if (lock2.isHeldByCurrentThread()) lock2.unlock();
            }
        };

        Thread t1 = new Thread(runnable,"t1");
        Thread t2 = new Thread(runnable,"t2");

        t1.start();t2.start();
        Thread.sleep(1000);
        //以中斷的方式解決死鎖問題
        t2.interrupt();
    }

鎖申請等待限時

可使用 tryLock()或者tryLock(long timeout, TimeUtil unit) 方法進行一次限時的鎖等待。code

  • tryLock(),線程嘗試獲取鎖,若是獲取到鎖則繼續執行,若是鎖被其餘線程持有,則當即返回false,也就是不會使當前線程等待,因此不會產生死鎖。
  • tryLock(long timeout, TimeUtil unit),表示在指定時長內獲取到鎖則繼續執行,若是等待指定時長後尚未獲取到鎖則返回false。
public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Runnable runnable = ()->{
            try {
                if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待1秒
                    Thread.sleep(2000);
                } else {
                    System.err.println(Thread.currentThread().getName() + "獲取鎖失敗!");
                }
            } catch (Exception e) {
                if (lock.isHeldByCurrentThread()) lock.unlock();
            }
        };
        Thread t1 = new Thread(runnable,"t1");
        Thread t2 = new Thread(runnable,"t2");
        t1.start();t2.start();
    }

ReentrantLock 配合 Condition 使用

配合關鍵字synchronized使用的方法如:await()、notify()、notifyAll(),一樣配合ReentrantLock 使用的Conditon提供瞭如下方法:對象

public interface Condition {
    void await() throws InterruptedException; // 相似於Object.wait()
    void awaitUninterruptibly(); // 與await()相同,但不會再等待過程當中響應中斷
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal(); // 相似於Obejct.notify()
    void signalAll();
}

ReentrantLock 配合 Condition的例子隊列

public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock(true);
        Condition condition = lock.newCondition();
        Thread t = new Thread(()->{
            try {
                lock.lock();
                System.err.println(Thread.currentThread().getName() + "-線程開始等待...");
                condition.await();
                System.err.println(Thread.currentThread().getName() + "-線程繼續進行了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t1");
        t.start();
        Thread.sleep(1000);
        System.err.println("過了1秒後...");
        lock.lock();
        condition.signal(); // 調用該方法前須要獲取到建立該對象的鎖不然會產生java.lang.IllegalMonitorStateException異常
        lock.unlock();
    }

ReentrantLock函數列表

// 建立一個 ReentrantLock ,默認是「非公平鎖」。
ReentrantLock()
// 建立策略是fair的 ReentrantLock。fair爲true表示是公平鎖,fair爲false表示是非公平鎖。
ReentrantLock(boolean fair)
// 查詢當前線程保持此鎖的次數。
int getHoldCount()
// 返回目前擁有此鎖的線程,若是此鎖不被任何線程擁有,則返回 null。
protected Thread getOwner()
// 返回一個 collection,它包含可能正等待獲取此鎖的線程。
protected Collection<Thread> getQueuedThreads()
// 返回正等待獲取此鎖的線程估計數。
int getQueueLength()
// 返回一個 collection,它包含可能正在等待與此鎖相關給定條件的那些線程。
protected Collection<Thread> getWaitingThreads(Condition condition)
// 返回等待與此鎖相關的給定條件的線程估計數。
int getWaitQueueLength(Condition condition)
// 查詢給定線程是否正在等待獲取此鎖。
boolean hasQueuedThread(Thread thread)
// 查詢是否有些線程正在等待獲取此鎖。
boolean hasQueuedThreads()
// 查詢是否有些線程正在等待與此鎖有關的給定條件。
boolean hasWaiters(Condition condition)
// 若是是「公平鎖」返回true,不然返回false。
boolean isFair()
// 查詢當前線程是否保持此鎖。
boolean isHeldByCurrentThread()
// 查詢此鎖是否由任意線程保持。
boolean isLocked()
// 獲取鎖。
void lock()
// 若是當前線程未被中斷,則獲取鎖。
void lockInterruptibly()
// 返回用來與此 Lock 實例一塊兒使用的 Condition 實例。
Condition newCondition()
// 僅在調用時鎖未被另外一個線程保持的狀況下,才獲取該鎖。
boolean tryLock()
// 若是鎖在給定等待時間內沒有被另外一個線程保持,且當前線程未被中斷,則獲取該鎖。
boolean tryLock(long timeout, TimeUnit unit)
// 試圖釋放此鎖。
void unlock()
相關文章
相關標籤/搜索