前言:對(duì)于一個(gè)控制鎖的業(yè)務(wù)場(chǎng)景來(lái)說(shuō),有簡(jiǎn)單的也有復(fù)雜的,最簡(jiǎn)單的就是判斷一個(gè)對(duì)象是否是null差导。再?gòu)?fù)雜點(diǎn)就是對(duì)于一個(gè)復(fù)雜條件的判斷被啼。
判斷的話如果是一個(gè)boolean類型帜消,guava提供了一個(gè)監(jiān)視器類來(lái)實(shí)現(xiàn),
相比傳統(tǒng)java提供的ReentrantLock,synchronized,他提供了很大的便利性浓体。好泡挺,我們一探窺見。
1命浴、Monitor介紹
此類旨在代替ReentrantLock娄猫。與使用的代碼相比,使用的代碼Monitor 不易出錯(cuò)且可讀性強(qiáng)ReentrantLock生闲,而不會(huì)造成明顯的性能損失媳溺。
Monitor通過(guò)優(yōu)化條件的評(píng)估和信號(hào)傳遞,甚至具有提高性能的潛力碍讯。信令是完全 隱式的悬蔽。通過(guò)消除顯式的信號(hào)傳遞,
此類可以保證在條件變?yōu)檎鏁r(shí)不會(huì)喚醒一個(gè)線程(不會(huì)由于使用引起“信號(hào)風(fēng)暴” Condition.signalAll)捉兴,
并且不會(huì)丟失信號(hào)(由于對(duì)的不正確使用而不會(huì)導(dǎo)致“掛起” Condition.signal)蝎困。
在調(diào)用任何具有void返回類型的enter方法時(shí),應(yīng)始終緊隨其后的是try / finally塊倍啥,以確保當(dāng)前線程干凈地離開監(jiān)視器:
// 實(shí)現(xiàn)就是包裝了重入鎖的lock.lock()
monitor.enter();
try {
// do things while occupying the monitor
} finally {
monitor.leave();
}
對(duì)任何帶有boolean返回類型的enter方法的調(diào)用應(yīng)始終作為包含try / finally塊的if語(yǔ)句的條件出現(xiàn)禾乘,以確保當(dāng)前線程干凈地離開監(jiān)視器:
// 實(shí)現(xiàn)就是包裝了重入鎖的lock.tryLock()
if (monitor.tryEnter()) {
try {
// do things while occupying the monitor
} finally {
monitor.leave();
}
} else {
// do other things since the monitor was not available
}
1、與synchronized逗栽、ReentrantLock比較
下面的例子顯示使用表達(dá)一個(gè)簡(jiǎn)單的線程持有人synchronized盖袭, ReentrantLock和Monitor。
- synchronized
該版本是最少的代碼行,主要是因?yàn)樗褂玫耐綑C(jī)制已內(nèi)置在語(yǔ)言和運(yùn)行時(shí)中鳄虱。但是程序員必須記住要避免幾個(gè)常見的錯(cuò)誤:wait()必須在while而不是if弟塞,并且 notifyAll()必須使用,notify()因?yàn)楸仨毜却齼蓚€(gè)不同的邏輯條件拙已。
public class SafeBox<V> {
private V value;
public synchronized V get() throws InterruptedException {
while (value == null) {
wait();
}
V result = value;
value = null;
notifyAll();
return result;
}
public synchronized void set(V newValue) throws InterruptedException {
while (value != null) {
wait();
}
value = newValue;
notifyAll();
}
}
- ReentrantLock
該版本比synchronized版本更為冗長(zhǎng)决记,并且仍然需要程序員記住要使用while而不是if。但是倍踪,一個(gè)優(yōu)點(diǎn)是我們可以引入兩個(gè)單獨(dú)的Condition對(duì)象系宫,這使我們可以使用signal()代替signalAll(),這可能會(huì)帶來(lái)性能上的好處建车。
public class SafeBox<V> {
private final ReentrantLock lock = new ReentrantLock();
private final Condition valuePresent = lock.newCondition();
private final Condition valueAbsent = lock.newCondition();
private V value;
public V get() throws InterruptedException {
lock.lock();
try {
while (value == null) {
valuePresent.await();
}
V result = value;
value = null;
valueAbsent.signal();
return result;
} finally {
lock.unlock();
}
}
public void set(V newValue) throws InterruptedException {
lock.lock();
try {
while (value != null) {
valueAbsent.await();
}
value = newValue;
valuePresent.signal();
} finally {
lock.unlock();
}
}
}
- Monitor
此版本在Guard對(duì)象周圍添加了一些詳細(xì)信息扩借,但從get和set方法中刪除了相同的詳細(xì)信息,甚至更多缤至。
Monitor實(shí)現(xiàn)了與上述ReentrantLock版本中手動(dòng)編碼相同的有效信令潮罪。
最后,程序員不再需要手動(dòng)編寫等待循環(huán)的代碼领斥,因此不必記住要使用while代替if嫉到。
public class SafeBox<V> {
private final Monitor monitor = new Monitor();
private final Monitor.Guard valuePresent = new Monitor.Guard(monitor) {
public boolean isSatisfied() {
return value != null;
}
};
private final Monitor.Guard valueAbsent = new Monitor.Guard(monitor) {
public boolean isSatisfied() {
return value == null;
}
};
private V value;
public V get() throws InterruptedException {
monitor.enterWhen(valuePresent);
try {
V result = value;
value = null;
return result;
} finally {
monitor.leave();
}
}
public void set(V newValue) throws InterruptedException {
monitor.enterWhen(valueAbsent);
try {
value = newValue;
} finally {
monitor.leave();
}
}
}
2、Monitor原理
- 首先得了解下Monitor結(jié)構(gòu)
private final boolean fair;
private final ReentrantLock lock;
private Guard activeGuards = null;
從上面結(jié)構(gòu)可以看出來(lái)月洛,Monitor也有公平非公平之分何恶,因?yàn)樗讓右彩腔趌ock封裝的,比較創(chuàng)新
的是有個(gè)activeGuards的Guard嚼黔,那么得再仔細(xì)了解下Guard類细层。
- Guard類結(jié)構(gòu)
final Monitor monitor;
final Condition condition;
int waiterCount = 0;
Guard next;
public abstract boolean isSatisfied();
警衛(wèi)類是依賴一個(gè)monitor,沒(méi)有monitor也就沒(méi)有必要警衛(wèi)了隔崎。
condition的作用就是關(guān)聯(lián)一個(gè)鎖條件今艺,鎖條件的實(shí)現(xiàn)是重寫抽象方法isSatisfied韵丑。
waiterCount爵卒,意思是重入的次數(shù),其實(shí)就是想知道是第一次還是最后一次撵彻,最后一次需要替換next指針钓株。
結(jié)構(gòu)看明白了,那么進(jìn)入正題陌僵,看下如何做到加鎖和寫鎖轴合。
- Monitor加鎖
已enterWhen為例:
public void enterWhen(Guard guard) throws InterruptedException {
// null判斷,沒(méi)什么好說(shuō)的
if (guard.monitor != this) {
throw new IllegalMonitorStateException();
}
// 減少指針引用路徑
final ReentrantLock lock = this.lock;
// 鎖是否被當(dāng)前線程持有
boolean signalBeforeWaiting = lock.isHeldByCurrentThread();
// 嘗試獲取鎖
lock.lockInterruptibly();
boolean satisfied = false;
try {
// 警衛(wèi)是否安全碗短,不安全則等待
if (!guard.isSatisfied()) {
// 等待警衛(wèi)通知
await(guard, signalBeforeWaiting);
}
satisfied = true;
} finally {
if (!satisfied) {
leave();
}
}
}
private void await(Guard guard, boolean signalBeforeWaiting) throws InterruptedException {
// 等待是否先通知受葛,當(dāng)前線程已經(jīng)拿到鎖了,進(jìn)行看下一個(gè)等待對(duì)象
if (signalBeforeWaiting) {
signalNextWaiter();
}
// 第一次開始等待,就是記錄下waiterCount
beginWaitingFor(guard);
try {
do {
// 第一次開始await
guard.condition.await();
// 看條件总滩,其實(shí)和那種最普通的寫法是一樣的
} while (!guard.isSatisfied());
} finally {
// 記錄下waiterCount纲堵,判斷是否需要執(zhí)行next警衛(wèi)
endWaitingFor(guard);
}
}
private void signalNextWaiter() {
for (Guard guard = activeGuards; guard != null; guard = guard.next) {
if (isSatisfied(guard)) {
guard.condition.signal();
break;
}
}
}
private void beginWaitingFor(Guard guard) {
int waiters = guard.waiterCount++;
if (waiters == 0) {
// push guard onto activeGuards
guard.next = activeGuards;
activeGuards = guard;
}
}
private void endWaitingFor(Guard guard) {
int waiters = --guard.waiterCount;
if (waiters == 0) {
// unlink guard from activeGuards
for (Guard p = activeGuards, pred = null; ; pred = p, p = p.next) {
if (p == guard) {
if (pred == null) {
activeGuards = p.next;
} else {
pred.next = p.next;
}
p.next = null; // help GC
break;
}
}
}
}
- Monitor解鎖
解鎖相對(duì)加鎖步驟少了很多,finally里面進(jìn)行unlock釋放鎖
/**
* Leaves this monitor. May be called only by a thread currently occupying this monitor.
*/
public void leave() {
final ReentrantLock lock = this.lock;
try {
// No need to signal if we will still be holding the lock when we return
if (lock.getHoldCount() == 1) {
signalNextWaiter();
}
} finally {
lock.unlock(); // Will throw IllegalMonitorStateException if not held
}
}
寫在最后
這里就簡(jiǎn)單分析下Monitor的實(shí)現(xiàn)了闰渔,點(diǎn)到為止席函,可以看出通過(guò)抽象Monitor和Guard,把鎖條件進(jìn)行封裝冈涧,有點(diǎn)策略和單個(gè)責(zé)任鏈模式的意思茂附,
這么想可能是google程序員覺(jué)得jdk的lock還是不夠抽象,所以再封裝了一層督弓。
寫這篇文章也就花了半個(gè)多小時(shí)的時(shí)間营曼,發(fā)現(xiàn)3篇文章一寫確實(shí)越來(lái)越順了,也有可能分析的還是過(guò)于表面愚隧,但是確實(shí)寫完比看完一個(gè)東西能理解更深入溶推。
這里感覺(jué)有個(gè)學(xué)習(xí)深度的總結(jié)還真有道理。
知識(shí)學(xué)習(xí)的層次是:看懂 < 說(shuō)出來(lái) < 寫出來(lái)并能讓別人也懂
本文由猿必過(guò) YBG 發(fā)布
禁止未經(jīng)授權(quán)轉(zhuǎn)載奸攻,違者依法追究相關(guān)法律責(zé)任
如需授權(quán)可聯(lián)系:zhuyunhui@yuanbiguo.com