重入鎖
重入鎖可以說是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()用於喚醒所有在等待中的線程。