synchronized的功能的擴展:重入鎖

重入鎖

重入鎖可以說是synchronized,Object.wait(),Object.notify()的一種替代品。

在JDK5的早期版本,重入鎖的新能要比synchronized好很多,在JDK6後對synchronized進行可很多優化,使得他和重入鎖的性能差距並不大。

重入鎖使用java.util.concurrent.locks.ReentrantLock類實現,下面我麼來看下重入鎖的簡單使用案例:

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
public class ReenterLock implements Runnable {
    public static ReentrantLock lock=new ReentrantLock();
    public static int i=0;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int j=0;j<10000000;j++) {
            lock.lock();
            try {
                i++;
            }finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ReenterLock tl=new ReenterLock();
        Thread t1=new Thread(tl);
        Thread t2=new Thread(tl);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }

}

運行程序可以得到結果爲20000000.

重入鎖具有很高的靈活性,需要開發人員手動用lock()與unlock()函數來指定何時加鎖何時解鎖,但是需要注意的是,在離開臨界區的時候要記得釋放鎖,否則其他線程就沒有機會在訪問臨界區了。(之所以叫重入鎖是因爲這種鎖可以反覆進入,但是記得一個線程同時獲得多少鎖,也必須釋放相同的次數)

重入鎖除了使用上的靈活性,還有一些高級的功能。

中斷響應

對於synchronized來說,如果一個線程在等待鎖,那麼結果只有兩種情況,要麼獲得鎖繼續執行,要麼保持等待。但是使用重入鎖。則提供了另外一種可能,就是線程可以被中斷。也就是說,在等待鎖的過程中,線程可以取消對鎖的請求。有些時候這麼做是很有必要的,比如和好朋友約好去打球,等了半小時沒到,街道電話得知朋友臨時有事,不能來了,那麼就打道回府了。這種情況對於處理死鎖有一定的幫助。使用lockInterruptibly()函數表示重入鎖可以響應中斷。

鎖申請限時等待

除了等待外部通知外,要避免死鎖還有一種辦法,就是限時等待。我麼可以通過tryLock方法進行一次限時的等待。

try{
    if(lock.tryLock(5,TimeUnit.SECONDS)){
        Thread.sleep(6000);
        }else{
            System.out.println("get lock failed");
        }
    }catch(InterruptdeException e){
        e.printStackTrace();
    }finally{
    if(lock.isHeldByCurrentThread()) lock.unlock();}
}

上面代碼中,設置的5秒的限時等待,由於睡眠了6秒,或導致請求鎖失敗。
tryLock也可以不帶參數進行運行,這種情況下,線程會嘗試獲得鎖,如果鎖未被其他線程佔用,申請鎖成功,返回true,否則申請失敗,線程也不會進行等待,直接返回false。

公平鎖

大多數情況下鎖是不公平的,也就是說不一定先請求就先獲得鎖,使用synchronized關鍵字實現鎖,鎖就是不公平的,重入鎖可以實現公平鎖,避免現象,也就是說,只要你排隊,就能獲得鎖,但是由於需要維護一個有序隊列,導致公平鎖的實現成本比較高,性能相對也非常低下。重入鎖有如下一個構造函數:
public ReentrantLock(boolean fair)
當參數爲true時鎖是公平的的。

重入鎖的好搭檔:Condition條件

Condition與Object.wait(),Object.notify()方法的作用大致相同,只不過是用來與重入鎖相關聯的。通過Lock接口(出入鎖就實現樂這一接口)的Condition newCondition()方法可以生成與當前重入鎖對象綁定的Condition實例,利用Condition對象,我們可以讓線程在特定的時刻進行等待,或者在某一個特定的時刻得到通知,繼續執行。
Condition接口提供的方法如下。

void await() throws InterrupttedExceptionI;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterrupttedExceptionI;
boolean await(long time,TimeUnit unit) throws InterrupttedExceptionI;
boolean  awaitUntil(Date deadline)  throws InterrupttedExceptionI;
void signal();
void siganlAll();

以上個方法的含義如下:

  • await()方法會使當前線程等待,同時會釋放鎖,當其他線程中使用signal()或signalAll()方法的時候,線程會重新獲得鎖繼續執行。或者線程被中斷是也能跳出等待。
  • awaitUninterruptibly()方法與await()方法基本相同,但是他不會在等待過程中相應或者相應中斷。
  • signal()用於喚醒一個在等待中的線程,同理signalAll()用於喚醒所有在等待中的線程。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章