reentrantLock 踩窖、 condition 是 JAVA 1.6 時(shí)推出的势就,也是用來(lái)實(shí)現(xiàn)多線程同步的杀饵,和 synchronized 干的事一樣底哥,用法頁(yè)差不多咙鞍,但是比 synchronized 要靈活
其中 reentrantLock 叫重入鎖,condition 是阻塞條件趾徽,我們直接啊看用法來(lái)了解這2個(gè)類
實(shí)現(xiàn)鎖的功能
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ (" " + (i + 1)));
}
lock.unlock();
}
}
synchronized 因?yàn)楸旧硎顷P(guān)鍵字续滋,只能實(shí)現(xiàn)標(biāo)記,本身不含鎖孵奶,需要我們指定對(duì)象鎖疲酌。而新的 API ReentrantLock 本身就是把鎖,不用再依托別人了。
ReentrantLock 在使用上類似于 synchronized 的同步代碼塊朗恳,我們直接在需要的位置湿颅, new 一個(gè) ReentrantLock 鎖出來(lái),然后 lock() 就能鎖定同步粥诫,unlock() 就能解鎖同步油航,注意unlock() 方法推薦寫在 finnaly 里面
Condition 來(lái)阻塞線程并釋放鎖
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition=lock.newCondition();
public void testMethod() {
try {
lock.lock();
System.out.println("開始wait");
condition.await();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ (" " + (i + 1)));
}
} catch (InterruptedException e) {
// TODO 自動(dòng)生成的 catch 塊
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
}
Condition 的 await() 會(huì)阻塞當(dāng)前線程,并釋放鎖怀浆、signal() 方法喚醒 wait 阻塞的線程谊囚。
Condition.awiat() = Object.wait()
Condition.signal() = Object.notify()
Condition.signalAll() = Object.notifyAll()
reentrantLock 和 condition 都是成對(duì)出現(xiàn),一個(gè) reentrantLock 里面可以有多個(gè) condition 阻塞條件對(duì)象揉稚,需要知道的是 conditionA await 阻塞的線程對(duì)象會(huì)放在這個(gè) conditionA 的阻塞隊(duì)列里秒啦,也只能由 conditionA signalAll 喚醒。換一個(gè) condition 對(duì)象 conditionB 是不管用的
線程池的阻塞隊(duì)列里面大量使用了 reentrantLock 搀玖,condition 余境,一般我們直接用 reentrantLock 、 condition 的時(shí)候比較少灌诅,雖然 reentrantLock 的性能比 synchronized 要好芳来,但是在使用上沒(méi)有 synchronized 方便啊,synchronized 關(guān)鍵字乙方就完了猜拾,我們就不用管了即舌。不過(guò) reentrantLock 、 condition 的 API 我們要熟挎袜,很多時(shí)候看別人的代碼時(shí)會(huì)用到顽聂。
ReentrantLock類的方法
getHoldCount()
查詢當(dāng)前線程保持此鎖的次數(shù),也就是執(zhí)行此線程執(zhí)行l(wèi)ock方法的次數(shù)getQueueLength()
返回正等待獲取此鎖的線程估計(jì)數(shù)盯仪,比如啟動(dòng)10個(gè)線程紊搪,1個(gè)線程獲得鎖,此時(shí)返回的是9getWaitQueueLength(Condition condition)
返回等待與此鎖相關(guān)的給定條件的線程估計(jì)數(shù)全景。比如10個(gè)線程耀石,用同一個(gè)condition對(duì)象,并且此時(shí)這10個(gè)線程都執(zhí)行了condition對(duì)象的await方法爸黄,那么此時(shí)執(zhí)行此方法返回10hasWaiters(Condition condition)
查詢是否有線程等待與此鎖有關(guān)的給定條件(condition)滞伟,對(duì)于指定contidion對(duì)象,有多少線程執(zhí)行了condition.await方法hasQueuedThread(Thread thread)
查詢給定線程是否等待獲取此鎖hasQueuedThreads()
是否有線程等待此鎖isFair()該鎖是否公平鎖
isHeldByCurrentThread() 當(dāng)前線程是否保持鎖鎖定炕贵,線程的執(zhí)行l(wèi)ock方法的前后分別是false和true
isLock()
此鎖是否有任意線程占用lockInterruptibly()
如果當(dāng)前線程未被中斷梆奈,獲取鎖tryLock()
嘗試獲得鎖,僅在調(diào)用時(shí)鎖未被線程占用称开,獲得鎖tryLock(long timeout TimeUnit unit)
如果鎖在給定等待時(shí)間內(nèi)沒(méi)有被另一個(gè)線程保持鉴裹,則獲取該鎖
Lock類分公平鎖和不公平鎖,公平鎖是按照加鎖順序來(lái)的,非公平鎖是不按順序的径荔,也就是說(shuō)先執(zhí)行l(wèi)ock方法的鎖不一定先獲得鎖
reentrantLock 的確要靈活的多,我們不用使用別的鎖了脆霎,在想要的位置直接 new 一把鎖总处,阻塞的條件可以有多個(gè),可以實(shí)現(xiàn)復(fù)雜的操作睛蛛,這是阻塞隊(duì)列的核心鹦马。另外 reentrantLock 可以使用 tryLock 嘗試獲得鎖,可以避免一直卡在這里競(jìng)爭(zhēng)鎖忆肾。reentrantLock 對(duì)于高玩來(lái)說(shuō)是個(gè)可以極大發(fā)揮的東西荸频,但是對(duì)于 coder 們來(lái)說(shuō),還是在項(xiàng)目縱謹(jǐn)慎使用客冈。
下面是一個(gè)對(duì) ReentrantLock 中肯的評(píng)價(jià)
既然如此旭从,我們什么時(shí)候才應(yīng)該使用 ReentrantLock 呢?答案非常簡(jiǎn)單 —— 在確實(shí)需要一些 synchronized 所沒(méi)有的特性的時(shí)候场仲,比如時(shí)間鎖等候和悦、可中斷鎖等候、無(wú)塊結(jié)構(gòu)鎖渠缕、多個(gè)條件變量或者鎖投票鸽素。 ReentrantLock 還具有可伸縮性的好處,應(yīng)當(dāng)在高度爭(zhēng)用的情況下使用它亦鳞,但是請(qǐng)記住馍忽,大多數(shù) synchronized 塊幾乎從來(lái)沒(méi)有出現(xiàn)過(guò)爭(zhēng)用,所以可以把高度爭(zhēng)用放在一邊燕差。我建議用 synchronized 開發(fā)遭笋,直到確實(shí)證明 synchronized 不合適,而不要僅僅是假設(shè)如果使用 ReentrantLock “性能會(huì)更好”谁不。請(qǐng)記住坐梯,這些是供高級(jí)用戶使用的高級(jí)工具。(而且刹帕,真正的高級(jí)用戶喜歡選擇能夠找到的最簡(jiǎn)單工具吵血,直到他們認(rèn)為簡(jiǎn)單的工具不適用為止。)偷溺。一如既往蹋辅,首先要把事情做好,然后再考慮是不是有必要做得更快挫掏。