Lock與ReentrantLock
ReentrantLock實現(xiàn)了Lock接口,并提供了與synchronized相同的互斥性和內(nèi)存可見性伸眶,在獲取ReentrantLock時纱皆,有著與同步代碼塊相同的內(nèi)存語義猴贰,在釋放ReentrantLock時揭绑,有著與退出同步代碼塊相同的內(nèi)存語義坦报。此外上煤,與synchronized一樣休玩,ReentrantLock提供了可重入的加鎖語義。
兩者比較:
1 顯式鎖提供定時的鎖等待劫狠、可中斷的鎖等待拴疤、公平性,以及非塊結(jié)構(gòu)的加鎖独泞。性能提升呐矾;
2 內(nèi)置鎖更簡潔,使用風(fēng)險更低懦砂,顯式鎖需要顯式的調(diào)用方法釋放鎖蜒犯。
3 未來性能可能持續(xù)提升的是內(nèi)置鎖,因為內(nèi)置鎖是JVM的內(nèi)置屬性荞膘,它能執(zhí)行一些優(yōu)化罚随。
4 Lock無法像內(nèi)置鎖那樣自動釋放,需要顯式釋放鎖羽资,增加了開發(fā)應(yīng)用的風(fēng)險淘菩。
public interface Lock
{
//獲取鎖
void lock();
//如果當(dāng)前線程未被中斷,則獲取鎖
void lockInterruptibly() throws
InterruptedException;
//如果鎖可用屠升,則獲取鎖潮改,并立即返回值 true。如果鎖不可用腹暖,則此方法將立即返回值 false汇在。
boolean tryLock();
//如果鎖在給定的等待時間內(nèi)空閑,并且當(dāng)前線程未被中斷脏答,則獲取鎖
boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException;
//釋放鎖
void unlock();
//返回綁定到此Lock實例的新Condition實例
Condition newCondition();
}
示例一:使用ReentantLock保護對象狀態(tài)的典型用法:
Lock lock = new ReentrantLock();
...
lock.lock();
try
{
//更新對象狀態(tài)
//捕獲異常趾疚,并在必要時恢復(fù)不變性條件
}
finally
{
//必須在finally中顯式的釋放鎖
lock.unlock();
}
示例二:通過tryLock來避免順序死鎖:
private void transfer(Account fromAccount, Account toAccount, int transferAmount, long deadTime)
{
long starttime = System.currentTimeMillis();
while(true)
{
//嘗試獲取鎖缨历,如果失敗,則循環(huán)嘗試糙麦,直到獲取鎖或者超時
if(fromAccount.getLock().tryLock())
{
try
{
if(toAccount.getLock().tryLock())
{
try
{
fromAccount.withDraw(transferAmount);
toAccount.deposit(transferAmount);
count++;
//System.out.println("run count = " + count);
}
finally
{
fromAccount.getLock().unlock();
}
}
}
finally
{
fromAccount.getLock().unlock();
}
}
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
};
if((System.currentTimeMillis() - starttime) < deadTime)
{
return;
}
}
}
定時鎖與輪詢鎖的鎖獲取模式是由tryLock實現(xiàn)的辛孵。與內(nèi)置鎖相比,使用tryLock如果不能獲取需要的鎖赡磅,可以使用可定時的或可輪詢的鎖獲取方式魄缚,從而重新獲得控制權(quán),它會釋放已經(jīng)獲得的鎖焚廊,然后重新嘗試獲取所有鎖(或者至少會記錄失敗日志冶匹,或采取其他措施)。
示例三:可中斷的鎖獲取方式lockInterruptibly:
//todo
lockInterruptibly方法能夠在獲得鎖的同時保持對中斷的響應(yīng)咆瘟。
僅當(dāng)內(nèi)置鎖不能滿足要求時嚼隘,才應(yīng)該考慮使用ReentrantLock的一些高級功能,包括:可定時的袒餐、可輪詢的與可中斷的鎖獲取操作飞蛹,公平隊列,以及非塊結(jié)構(gòu)的鎖灸眼。否則卧檐,還是應(yīng)該優(yōu)先使用synchronized。
公平鎖
ReentrantLock構(gòu)造函數(shù)提供了兩種公平性選擇:非公平鎖(默認(rèn))或者公平鎖焰宣。
公平鎖:線程將按照它們發(fā)出請求的順序來獲得鎖霉囚;
非公平鎖:允許“插隊”,如果在發(fā)出請求的同時匕积,該鎖的狀態(tài)變?yōu)榭捎糜蓿敲催@個線程將跳過隊列中所有的等待線程并獲得這個鎖。只有當(dāng)鎖被某個線程持有時闪唆,新發(fā)出請求的線程才會被放入隊列暖呕;
大多數(shù)情況下,非公平鎖的性能高于公平鎖:
1 公平鎖由于在掛起線程和恢復(fù)線程時的開銷而極大的降低性能苞氮;
2 恢復(fù)一個掛起的線程與該線程真正開始運行之間存在嚴(yán)重的延遲湾揽;
當(dāng)持有鎖的時間相對較長,或者請求鎖的平均時間間隔較長笼吟,那么應(yīng)該使用公平鎖库物。
讀寫鎖
ReentrantLock的互斥規(guī)則過于強硬〈铮可通過讀寫鎖進行優(yōu)化戚揭。
一個資源可以被多個讀操作訪問,或者被一個寫操作訪問撵枢,但兩者不能同時進行民晒。
接口:ReadWriteLock
實現(xiàn)類:ReentrantReadWriteLock
public interface ReadWriteLock
{
Lock readLock();
Lock writeLock();
}