狀態依賴性
定義:只有滿足特定的狀態才能繼續執行某些操作(這些操作依賴于固定的狀態,這些狀態需要等待別的線程來滿足)。
FutureTask,Semaphroe,BlockingQueue等,都是狀態依賴性的類。
條件隊列
條件對列:條件對列就是由于不滿足繼續的條件而被wait操作阻塞的線程隊列。他們都在等待條件滿足,然后被喚醒。
條件謂詞:狀態依賴性依賴的前提條件。如BlockingQueue中的isFull,isEmpty等。
條件等待中存在三個要素:加鎖 + 條件謂詞 + wait方法
wait方法和notify方法
我理解的wait方法:會釋放鎖+阻塞當前線程,放入條件對列,等待被喚醒,喚醒后,需要重新獲得鎖,獲得鎖之后繼續執行wait那句代碼所在的位置(即使wait在鎖塊的中間代碼部分)。
notify(All)方法:只是喚醒條件隊列中的線程。但是不釋放鎖。
使用wait notify方法的時候,一定要持有條件對列所屬的鎖。
使用輪詢和休眠實現簡單的狀態依賴性阻塞
- while(true)
- ????????{
- ????????????//這里不對循環上鎖,不然這個鎖就無法釋放了,不對休眠上鎖,休眠上鎖,在休眠的時候別人也無法操作,永遠都不可能有元素出去
- ????????????synchronized (this)
- ????????????{
- ????????????????//如果隊列不是滿的,那么就放入元素
- ????????????????if(!this.isFull())
- ????????????????{
- ????????????????????this.doPut(v);
- ????????????????????return;
- ????????????????}
- ????????????}
- ????????????//否則休眠,退出cpu占用
- ????????????Thread.sleep(SLEEP_GRANULARITY);
- ????????}
- ????}
使用條件隊列來實現狀態依賴性阻塞
?
- public synchronized void put(V v) throws InterruptedException
- ???{
- ???????while(this.isFull())
- ???????{
- ???????????//這里掛起程序,會釋放鎖
- ???????????this.wait();
- ???????}
- ???????//如果隊列不為滿的,那么程序被喚醒之后從新獲取鎖
- ???????this.doPut(v);
- ???????//執行結束,喚醒其他隊列
- ???????this.notifyAll();
- ???}
注意上面要使用while。
對于監視器來說,wait操作產生的線程,都放在這個監視器唯一的條件隊列里。
如果使用Lock,可以使用condition來產生不同的條件對列。
注意上面的?this.notifyAll();代碼,將會喚醒這個監視器條件隊列里所有等待的線程。其實這里只用喚醒因為empty阻塞的線程,而不用喚醒因為full阻塞的線程。
如果使用?this.notify(),只會隨機喚醒一個,如果喚醒的是因為full堵塞的線程,那么就可能沒有正常喚醒。影響性能,甚至造成活躍性的危險。
這種情況下,可以使用Lock和Condition來改造。
- protected final Lock lock = new ReentrantLock();
- private final Condition notFull = lock.newCondition();
- private final Condition notEmpty = lock.newCondition();
- ?public void put(T x) throws InterruptedException {
- ????????lock.lock();
- ????????try {
- ????????????while (count == items.length)
- ????????????????notFull.await();
- ????????????items[tail] = x;
- ????????????if (++tail == items.length)
- ????????????????tail = 0;
- ????????????++count;
- ????????????notEmpty.signal();
- ????????} finally {
- ????????????lock.unlock();
- ????????}
- ????}
注意:這里不是signalAll。
閥門類
使用閉鎖CountDownLatch,傳入1的時候可以作為閥門開關。前提是在其他線程的第一步先執行開關的await。使用開關的countDown方法就可以打開開關。
但是這種閥門,只能打開,不能關閉。
使用wait和notifyAll來實現可重新關閉的閥門。
Condition
注意,由于Condition對象繼承自Object,它也有wait,notify,notifyAll方法,其實它對應方法名字應該是await,signal,signalAll。
?