本篇內(nèi)容主要講解“怎么使用monitor和ReentrantLock”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么使用monitor和ReentrantLock”吧!
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比謝家集網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式謝家集網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋謝家集地區(qū)。費(fèi)用合理售后完善,10余年實(shí)體公司更值得信賴。
先實(shí)現(xiàn)一個(gè)利用內(nèi)置鎖協(xié)調(diào)兩個(gè)線程的代碼。通過synchronized關(guān)鍵字通過內(nèi)部鎖進(jìn)行線程協(xié)調(diào),實(shí)際上幫助我們隱藏了“條件隊(duì)列”這樣一個(gè)概念。粗淺地講,內(nèi)部鎖就是一個(gè)可重入的互斥鎖。下面的代碼是利用內(nèi)部鎖的一個(gè)實(shí)現(xiàn)。
知識(shí)點(diǎn)細(xì)節(jié)見注釋:
public class FooBarByMonitor {
private int n;
public FooBarByMonitor(int n) {
this.n = n;
}
private final Object lock = new Object(); //_ 因?yàn)椴荒苄薷念}目的方法前面,所以不能使用默認(rèn)當(dāng)前對(duì)象的monitor。創(chuàng)建一個(gè)對(duì)象利用它的monitor。
private volatile boolean isFooTurn = true; //_ 定義一個(gè)boolean變量表示互斥的2種狀態(tài)。
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized (lock) {
while (!isFooTurn) lock.wait(); //_ 如果不該打印foo則阻塞(把當(dāng)前線程放到lock的等待隊(duì)列),等待通知。第一次是可打印的。
printFoo.run();
isFooTurn = false; //_ 更改到可以打印bar的狀態(tài)。
lock.notify(); //_ 因?yàn)橹挥袃蓚€(gè)線程,所以notify是可以的,否則應(yīng)使用notifyAll。
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized (lock) {
while (isFooTurn) lock.wait(); //_ 如果不該打印bar則阻塞,等待通知。
printBar.run();
isFooTurn = true; //_ 更改到可以打印foo的狀態(tài)。
lock.notify(); //_ 喚醒其它等待的線程。
}
}
}
}
前面文章《條件隊(duì)列是個(gè)線程的隊(duì)列》里提到Lock是內(nèi)部鎖的顯示化,Condition是內(nèi)部鎖條件隊(duì)列的顯示化。對(duì)比下面和上面的代碼,應(yīng)該可以體會(huì)到這一點(diǎn)。通過由Lock對(duì)象針對(duì)不同"條件謂詞"定義出不同等待隊(duì)列,可以做得比使用內(nèi)部鎖提供的多個(gè)條件謂詞共享一個(gè)等待隊(duì)列的模式更高效地進(jìn)行線程協(xié)調(diào)。所謂線程協(xié)調(diào)就是安排線程適當(dāng)?shù)淖枞?、喚醒、運(yùn)行的切換而已。
最開始學(xué)習(xí)“條件隊(duì)列”時(shí),我對(duì)“條件”這個(gè)詞感到莫名其妙。為什么不叫‘等待隊(duì)列’?‘條件’從何而來?
其實(shí)這要從 謂詞——Predicate 說起,可參考wiki定義。簡(jiǎn)單的說“謂詞”就是指那些返回真或假的表達(dá)式。而條件——Condition就是某事成真或假的前提。我們常用變量表示conditon的狀態(tài),這就是condition variable。
條件謂詞——Conditon Predicate,翻譯成人話就是最終用一個(gè)變量得出真、假的判斷。
那這跟多線程編程又有什么關(guān)系呢?關(guān)系還挺深的。并發(fā)編程的核心是協(xié)調(diào)線程的運(yùn)行,就是有時(shí)候一些線程可以運(yùn)行而另一些線程要暫停下來。那么根據(jù)什么來阻塞、喚醒線程呢?這就要根據(jù)你實(shí)際業(yè)務(wù)里的邏輯來決定,這個(gè)邏輯運(yùn)算的結(jié)果就是個(gè)boolean值。這個(gè)邏輯最終體現(xiàn)在condition variable、體現(xiàn)在Conditon Predicate上。對(duì)于下面這段輪流打印foobar的代碼,決定線程阻塞還是運(yùn)行的條件就是“該打印foo了嗎?”,我們用isFooTurn這個(gè)變量來表示。對(duì)于阻塞隊(duì)列的添加、刪除操作來說,對(duì)應(yīng)的條件變量就應(yīng)該能反應(yīng)這個(gè)隊(duì)列的滿、空狀態(tài)。多線程的協(xié)調(diào)就是合理的基于條件變量改變線程自身的狀態(tài)以及改變條件變量的狀態(tài)來完成的。
回到“條件隊(duì)列”這詞上來就容易了,條件隊(duì)列里裝的都是等待條件發(fā)生變化的線程。希望到此你跟我一樣對(duì)“條件隊(duì)列”這個(gè)詞再?zèng)]有違和感了。:)
知識(shí)點(diǎn)細(xì)節(jié)見注釋:
public class FooBarByLock {
private int n;
public FooBarByLock(int n) {
this.n = n;
}
//_ 這里相對(duì)使用Semaphore的版本多出一個(gè)狀態(tài)變量表示 應(yīng)該打印foo還是bar。
//_ 在Semaphore的版本里我們沒有多定義一個(gè)變量是因?yàn)?對(duì)foo信號(hào)量初始化為1,即實(shí)現(xiàn)了先打印1。
//_ 另外由于信號(hào)量交替acquire,release的特性保證了不需要額外定義一個(gè)狀態(tài)變量。
//_ 同時(shí),這個(gè)變量也是用于表達(dá)下面兩個(gè)條件隊(duì)列對(duì)應(yīng)'條件謂詞'的關(guān)鍵。
private volatile boolean isFooTurn = true;
private final Lock lock = new ReentrantLock();
private final Condition fooCondition = lock.newCondition(); //_ 對(duì)應(yīng)條件謂詞 'isFooTurn == true'
private final Condition barCondition = lock.newCondition(); //_ 對(duì)應(yīng)條件謂詞 'isFooTurn == false'
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try {
//_ 雖然在這道例題里可以使用if,但是如果有多個(gè)線程可以更改這個(gè)用于表達(dá)條件謂詞的變量isFooTurn就會(huì)出現(xiàn)問題。
while (!isFooTurn) { //_ 雖然使用針對(duì)此題是沒問題的,作為定式這里直接使用循環(huán)。
fooCondition.await(); //_ 等待打印foo的條件謂詞成立。因?yàn)閕sFooTurn初始化為true,所以第一輪不阻塞。
}
printFoo.run();
isFooTurn = false; //_ 更改條件謂詞,使另個(gè)線程可以繼續(xù)。
barCondition.signal(); //_ 通知bar的條件隊(duì)列,可以打印了。
} finally {
lock.unlock();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try {
while (isFooTurn) {
barCondition.await(); //_ 阻塞。等待打印bar的條件謂詞'isFooTurn == false'成立
}
printBar.run();
isFooTurn = true;
fooCondition.signal();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
FooBarByLock foobar = new FooBarByLock(20);
new Thread(() -> {
try {
foobar.foo(() -> System.out.print("foo"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
foobar.bar(() -> System.out.print("bar"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
到此,相信大家對(duì)“怎么使用monitor和ReentrantLock”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!