首页 > 代码库 > <Java><Multi-thread>

<Java><Multi-thread>

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>