wait和notify是Java最基本的線程間通訊機制,體現了線程的交互,用於信息的傳遞。例如在生產者消費者模式中,利用阻塞和喚醒使不一樣線程之間配合實現業務邏輯。java
阻塞階段--wait,調用對象的wait方法,線程進入WAITING狀態,阻塞掛起,釋放鎖。
wait阻塞後,直到下面狀況之一發生時,線程纔會被喚醒。ide
喚醒階段 --notify/notifyAll測試
synchronized(this) { while(條件){ wait(); } }
synchronized (resourceA) { synchronized (resourceB) { try { resourceA.wait(); // 只釋放resourceA鎖 } catch (InterruptedException e) { e.printStackTrace(); } } }
注意點:調用某個對象的notify,只能喚醒與該對象對應的線程。調用wait也只釋放當前的那把鎖。this
EntrySet:入口集;WaitSet:等待集;The owner:線程擁有鎖。’
鎖的運行原理:開始線程在入口集和等待集競爭鎖【1】,此時線程A獲取到了鎖【2】,入口集和等待集中的線程進入BLOCKED。此時A能夠正常運行完釋放鎖【6】,也能夠調用wait釋放鎖進入等待集【3】。等待集線程被喚醒【4】後,進入另外一個等待集,與入口集的線程一塊兒競爭鎖【5】。線程
生產者消費者模式能夠解耦生產者和消費者,使二者更好地配合。
設計
// 生產和消費100個產品 public class ProducerConsumerModelByWaitAndNotify { public static void main(String[] args) { // 建立倉庫 Storage storage = new Storage(); // 建立生產者消費者線程 Thread producer = new Thread(new ProducerTask(storage)); Thread consumer = new Thread(new ConsumerTask(storage)); producer.start(); consumer.start(); } } class ProducerTask implements Runnable { private Storage storage; public ProducerTask(Storage storage) { this.storage = storage; } @Override public void run() { // 生產100個產品 for (int i = 0; i < 100; i++) { storage.put(); } } } class ConsumerTask implements Runnable { private Storage storage; public ConsumerTask(Storage storage) { this.storage = storage; } @Override public void run() { for (int i = 0; i < 100; i++) { storage.take(); } } } class Storage { private int maxSize; private Queue<Date> storage; public Storage() { this.maxSize = 10; // 隊列最大是10 this.storage = new LinkedList<>(); } /** * wait和notify須要首先獲取到鎖,所以須要使用synchronized方法或者同步代碼塊 */ public synchronized void put() { // 倉庫已滿,沒法生產更多產品,讓出鎖 while (storage.size() == maxSize) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } storage.add(new Date()); System.out.println("生產者生產產品,此時倉庫產品數:" + storage.size()); // 通知消費者消費 notify(); } public synchronized void take() { // 倉庫爲空,沒法獲取到產品,線程讓出鎖 while (storage.size() == 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } storage.poll(); System.out.println("消費者消費產品,此時倉庫產品數:" + storage.size()); // 通知生產者生產 notify(); } }
主要是爲了讓通訊更加可靠,防止死鎖、永久等待的發生。code
wait放到synchronized代碼中對線程有必定的保護做用。假設沒有synchronized的保護,線程A在運行到wait語句以前,切換到線程B執行了notify語句,此時執行了wait語句釋放鎖後,沒有線程喚醒,致使了永久等待。對象
sleep方法是針對單個線程的,與其餘線程無關,無需放入到同步代碼塊中。blog
wait和notify是鎖級別操做,而鎖是屬於某個對象的,鎖標識在對象的對象頭中。若是將wait和notify定義在線程中,則會有很大的侷限性。例如每一個線程均可能會休眠。若是某個線程持有多個鎖,並且鎖之間是相互配合的時,wait方法在Thread類中,就沒有辦法實現線程的配合。隊列
我的理解: 調用線程對象的wait方法,也就是說以線程爲鎖。wait和notify的初衷就是用來線程間通訊,若是以線程爲鎖,不利於設計流程。