1. 程式人生 > ><Java><Multi-thread>

<Java><Multi-thread>

tac enter println ads 搶占 true 內部 char color

Overview

  • notify() VS notifyAll()

From a basic example

  • 一個最經典的生產者消費者模型:
  • public synchronized void put(String value) throws InterruptedException{
            while (buffer.size() == capacity) {
                System.out.println(Thread.currentThread().getName() + " is waiting...");
                wait();
            }
            buffer.add(value);
            System.out.println(Thread.currentThread().getName() + " has produced " + value);
            notify();
        }
    
        public synchronized String get() throws InterruptedException{
            while (buffer.size() == 0) {
                System.out.println(Thread.currentThread().getName() + " is waiting...");
                wait();
            }
            String val = buffer.remove(0);
            notify();
            System.out.println(Thread.currentThread().getName() + " has consumed " + val);
            return val;
        }
  • 首先,第一個問題是,為什麽get和put內部都要用while循環?答:We need a while loop in case we get this situation:
    • c1進入臨界區,發現size為空,於是wait;
    • p1進入臨界區,增加了buffer,並notify;
    • 同時,c2也在等待進入臨界區(block阻塞狀態);
    • c1被notified,但是c1未搶占到鎖,因為c2進入了臨界區,消費了buffer,之後buffer又為空;
    • 之後,c1擁有了鎖,但此時buffer已經為空,所以必須有while循環,再次判斷。
  • 然後,為什麽需要notifyAll()?
    • 我們都知道notify()是通知一個線程,而notifyAll()是通知所有線程。具體說來又有什麽區別呢?
    • 考慮一個buffer為1的隊列(for simplicity):
    • STEP 1:
      - P1 puts 1 char into the buffer

      STEP 2:
      - P2 attempts put - checks wait loop - already a char - waits 【P2在等待】

      STEP 3:
      - P3 attempts put - checks wait loop - already a char - waits 【P2、P3在等待】

      STEP 4:
      - C1 attempts to get 1 char
      - C2 attempts to get 1 char - blocks on entry to the get

      method 【C2阻塞】
      - C3 attempts to get 1 char - blocks on entry to the get method 【C2、C3阻塞】

      STEP 5:
      - C1 is executing the get method - gets the char, calls notify, exits method
      - The notify wakes up P2
      - BUT, C2 enters method before P2 can (P2 must reacquire the lock), so P2 blocks on entry to the put method 【P3等待】【P2、C3阻塞】
      - C2 checks wait loop, no more chars in buffer, so waits 【P3、C2等待】【P2、C3阻塞】
      - C3 enters method after C2, but before P2, checks wait loop, no more chars in buffer, so waits 【C3、C2、P3等待】【P2阻塞】

      STEP 6:
      - NOW: there is P3, C2, and C3 waiting!
      - Finally P2 acquires the lock, puts a char in the buffer, calls notify, exits method 【P2終於搶占了鎖】

      STEP 7:
      - P2‘s notification wakes P3 (remember any thread can be woken) 【P3被notified】
      - P3 checks the wait loop condition, there is already a char in the buffer, so waits. 【P3、C2、C3等待】【阻塞隊列為空】
      - NO MORE THREADS TO CALL NOTIFY and THREE THREADS PERMANENTLY SUSPENDED!

    • 上述死鎖問題的解決方案就是:把所有的notify()換成notifyAll()。從而每次都能notifyAll把所有在等待的線程都變成阻塞狀態。
  • 以上,實際上就是造成了blocked和wait這兩個狀態之間的區別。

FYI

  • stackOverflow

<Java><Multi-thread>