首先,wait、notify和notifyAll 這三個 都是Object類里的方法,可以用來控制線程的狀態
解釋:
如果對象調用了wait方法就會使持有該對象的線程把該對象的控制權交出去,然后處於等待狀態。
如果對象調用了notify方法就會通知某個正在等待這個對象的控制權的線程可以繼續運行。
如果對象調用了notifyAll方法就會通知所有等待這個對象控制權的線程繼續運行。
下來有個生產者消費者的列子, 提供理解:
package com.luxins.join.join; import java.util.Queue; public class Consumer implements Runnable { //背包 Queue<String> bages; //大小 int size; public Consumer(Queue<String> bages, int size) { this.bages = bages; this.size = size; } @Override public void run() { while (true) { synchronized (bages) { while (bages.isEmpty()) { try { System.out.println("背包滿了了"); bages.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //睡眠1s try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String bage = bages.remove(); System.out.println("當前消費者的數量為:" + bage); //喚醒生產者(因為是雙向的,又是同一個對象bages下) bages.notifyAll(); } } } }
package com.luxins.join.join; import java.util.Queue; public class producer implements Runnable { Queue<String> bages; int size; public producer(Queue<String> bages, int size) { this.bages = bages; this.size = size; } @Override public void run() { int i = 0; while (true) { i++; synchronized (bages) { while (bages.size() == size) { System.out.println("背包已經裝滿了"); try { bages.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("當前生產者數量為:"+i); bages.add("bages"+i); //喚醒消費者 bages.notifyAll(); } } } }
package com.luxins.join.join; import java.util.LinkedList; import java.util.Queue; public class TestMain { public static void main(String[] args) { Queue<String> a = new LinkedList<>(); int size = 10; Consumer consumer = new Consumer(a, size); producer producer = new producer(a, size); Thread t1 = new Thread(consumer); Thread t2 = new Thread(producer); t2.start(); t1.start(); } }
做多 10條,可能在沒滿 10之前 ,就已經 用了
情況是隨機的
還有就是不一定就是生產一條 一定會消費一條
有可能生產了兩條 只消費一條 然后繼續生產
cup 不同,在不同電腦上出現的 效果 就不一樣
這個例子就是
我們在調用wait()方法的時候,心里想的肯定是因為當前方法不滿足我們指定的條件,因此執行這個方法的線程需要等待直到其他線程改變了這個條件並且做出了通知。
那么為什么要把wait()方法放在循環而不是if判斷里呢,其實答案顯而易見,因為wait()的線程永遠不能確定其他線程會在什么狀態下notify(),
所以必須在被喚醒、搶占到鎖並且從wait()方法退出的時候再次進行指定條件的判斷,以決定是滿足條件往下執行呢還是不滿足條件再次wait()呢。
就像在本例中,如果只有一個生產者線程,一個消費者線程,那其實是可以用if代替while的,因為線程調度的行為是開發者可以預測的,
生產者線程只有可能被消費者線程喚醒,反之亦然,因此被喚醒時條件始終滿足,程序不會出錯。
但是這種情況只是多線程情況下極為簡單的一種,更普遍的是多個線程生產,多個線程消費,
那么就極有可能出現喚醒生產者的是另一個生產者或者喚醒消費者的是另一個消費者,這樣的情況下用if就必然會現類似過度生產或者過度消費的情況了,
典型如IndexOutOfBoundsException的異常。所以所有的java書籍都會建議開發者永遠都要把wait()放到循環語句里面。