溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用monitor和ReentrantLock

發(fā)布時間:2021-12-27 15:56:45 來源:億速云 閱讀:124 作者:iii 欄目:互聯(lián)網(wǎng)科技

本篇內(nèi)容主要講解“怎么使用monitor和ReentrantLock”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么使用monitor和ReentrantLock”吧!

先實現(xiàn)一個利用內(nèi)置鎖協(xié)調(diào)兩個線程的代碼。通過synchronized關(guān)鍵字通過內(nèi)部鎖進行線程協(xié)調(diào),實際上幫助我們隱藏了“條件隊列”這樣一個概念。粗淺地講,內(nèi)部鎖就是一個可重入的互斥鎖。下面的代碼是利用內(nèi)部鎖的一個實現(xiàn)。

知識點細節(jié)見注釋:

public class FooBarByMonitor {    private int n;
   public FooBarByMonitor(int n) {        this.n = n;    }
   private final Object lock = new Object();   //_ 因為不能修改題目的方法前面,所以不能使用默認當前對象的monitor。創(chuàng)建一個對象利用它的monitor。
   private volatile boolean isFooTurn = true;  //_ 定義一個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則阻塞(把當前線程放到lock的等待隊列),等待通知。第一次是可打印的。                printFoo.run();                isFooTurn = false;  //_ 更改到可以打印bar的狀態(tài)。                lock.notify();      //_ 因為只有兩個線程,所以notify是可以的,否則應使用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();     //_ 喚醒其它等待的線程。            }        }    }}

前面文章《條件隊列是個線程的隊列》里提到Lock是內(nèi)部鎖的顯示化,Condition是內(nèi)部鎖條件隊列的顯示化。對比下面和上面的代碼,應該可以體會到這一點。通過由Lock對象針對不同"條件謂詞"定義出不同等待隊列,可以做得比使用內(nèi)部鎖提供的多個條件謂詞共享一個等待隊列的模式更高效地進行線程協(xié)調(diào)。所謂線程協(xié)調(diào)就是安排線程適當?shù)淖枞?、喚醒、運行的切換而已。

最開始學習“條件隊列”時,我對“條件”這個詞感到莫名其妙。為什么不叫‘等待隊列’?‘條件’從何而來?

其實這要從 謂詞——Predicate 說起,可參考wiki定義。簡單的說“謂詞”就是指那些返回真或假的表達式。而條件——Condition就是某事成真或假的前提。我們常用變量表示conditon的狀態(tài),這就是condition variable。

條件謂詞——Conditon Predicate,翻譯成人話就是最終用一個變量得出真、假的判斷。

那這跟多線程編程又有什么關(guān)系呢?關(guān)系還挺深的。并發(fā)編程的核心是協(xié)調(diào)線程的運行,就是有時候一些線程可以運行而另一些線程要暫停下來。那么根據(jù)什么來阻塞、喚醒線程呢?這就要根據(jù)你實際業(yè)務里的邏輯來決定,這個邏輯運算的結(jié)果就是個boolean值。這個邏輯最終體現(xiàn)在condition variable、體現(xiàn)在Conditon Predicate上。對于下面這段輪流打印foobar的代碼,決定線程阻塞還是運行的條件就是“該打印foo了嗎?”,我們用isFooTurn這個變量來表示。對于阻塞隊列的添加、刪除操作來說,對應的條件變量就應該能反應這個隊列的滿、空狀態(tài)。多線程的協(xié)調(diào)就是合理的基于條件變量改變線程自身的狀態(tài)以及改變條件變量的狀態(tài)來完成的。

回到“條件隊列”這詞上來就容易了,條件隊列里裝的都是等待條件發(fā)生變化的線程。希望到此你跟我一樣對“條件隊列”這個詞再沒有違和感了。:)

知識點細節(jié)見注釋:

public class FooBarByLock {    private int n;
   public FooBarByLock(int n) {        this.n = n;    }
   //_ 這里相對使用Semaphore的版本多出一個狀態(tài)變量表示 應該打印foo還是bar。    //_ 在Semaphore的版本里我們沒有多定義一個變量是因為 對foo信號量初始化為1,即實現(xiàn)了先打印1。    //_ 另外由于信號量交替acquire,release的特性保證了不需要額外定義一個狀態(tài)變量。    //_ 同時,這個變量也是用于表達下面兩個條件隊列對應'條件謂詞'的關(guān)鍵。    private volatile boolean isFooTurn = true;
   private final Lock lock = new ReentrantLock();    private final Condition fooCondition = lock.newCondition(); //_ 對應條件謂詞 'isFooTurn == true'    private final Condition barCondition = lock.newCondition(); //_ 對應條件謂詞 'isFooTurn == false'
   public void foo(Runnable printFoo) throws InterruptedException {        for (int i = 0; i < n; i++) {            lock.lock();            try {                //_ 雖然在這道例題里可以使用if,但是如果有多個線程可以更改這個用于表達條件謂詞的變量isFooTurn就會出現(xiàn)問題。                while (!isFooTurn) {       //_ 雖然使用針對此題是沒問題的,作為定式這里直接使用循環(huán)。                    fooCondition.await();  //_ 等待打印foo的條件謂詞成立。因為isFooTurn初始化為true,所以第一輪不阻塞。                }                printFoo.run();                isFooTurn = false;     //_ 更改條件謂詞,使另個線程可以繼續(xù)。                barCondition.signal(); //_ 通知bar的條件隊列,可以打印了。            } 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();    }}

到此,相信大家對“怎么使用monitor和ReentrantLock”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI