本文介紹sleep()、wait()、notify()、notifyAll()方法,主要要理解:html
此方法是讓線程睡眠指定時間,不釋放鎖(睡覺,固然要上鎖,這個還用說麼)。java
此方法我貌似不多用,又彷佛很經常使用。由於,在正式代碼中我不多用到,而在測試代碼中,卻又常常用來模擬某某業務需時幾秒的阻塞。編程
public class Sleep { public static void main(String[] args) { System.out.println("Start..."); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("End..."); } }
通常,還有個更易讀的寫法,同樣的效果api
import java.util.concurrent.TimeUnit; public class Sleep { public static void main(String[] args) { System.out.println("Start..."); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("End..."); } }
線程轉到等待狀態,釋放鎖。(既然等待,固然得釋放鎖了,咱們在等待、迎接貴賓時,也是敞開着大門的,哈哈)oracle
在持有鎖的狀況下才能調用此方法,一般搭配外層的循環以判斷是否繼續等待。ide
wait方法能夠執行時間,也可不,由notify方法喚醒。測試
晚餐、客人、服務員的例子this
/** * 晚餐 */ public class Dinner { private String mainDish; // 主菜 public String getMainDish() { return mainDish; } public void setMainDish(String mainDish) { this.mainDish = mainDish; } }
/** * 客人 */ public class Customer extends Thread { private Dinner d; public Customer(Dinner d) { super(); this.d = d; } @Override public void run() { synchronized (d) { while (d == null || d.getMainDish() == null) { try { System.out.println(currentThread().getName() + ", customer start to wait."); d.wait(); System.out.println(currentThread().getName() + ", customer end to wait."); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } d.setMainDish(null); // 至關於把菜吃掉 System.out.println(currentThread().getName() + ", customer eat the food."); } super.run(); } }
/** * 服務員 */ public class Waiter extends Thread { private Dinner d; public Waiter(Dinner d) { super(); this.d = d; } @Override public void run() { synchronized (d) { d.notify(); d.setMainDish("牛扒"); // 至關於上菜 System.out.println(currentThread().getName() + ", waiter notify."); } super.run(); } }
import java.util.concurrent.TimeUnit; public class HowToUse { public static void main(String[] args) { Dinner d = new Dinner(); Customer c = new Customer(d); Waiter w = new Waiter(d); c.start(); /* 等待一段時間,目的讓Customer線程先啓動 */ try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } w.start(); } }
日誌:spa
Thread-0, customer start to wait. Thread-1, waiter notify. Thread-0, customer end to wait. Thread-0, customer eat the food.
固然,也可執行時長中止等待了:線程
public class Wait { public static void main(String[] args) { System.out.println("Start..."); String s = ""; synchronized (s) { try { s.wait(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("End..."); } }
一個是通知一個線程(通知哪個線程是不肯定的哦),另外一個是喚醒所有線程。
將HowToUse方法修改下:
import java.util.concurrent.TimeUnit; public class HowToUse { public static void main(String[] args) { Dinner d = new Dinner(); Customer c1 = new Customer(d); Customer c2 = new Customer(d); Waiter w = new Waiter(d); c1.start(); c2.start(); /* 等待一段時間,目的讓Customer線程先啓動 */ try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } w.start(); } }
就能夠看到:
Thread-0, customer start to wait.
Thread-1, customer start to wait.
Thread-2, waiter notify.
Thread-0, customer end to wait.
Thread-0, customer eat the food.
Thread-0、Thread-1分別表明兩個客人,waiter喚醒了Thread-0,屬於Thread-0的客人中止等待,去吃大餐了。
將notify修改爲notifyAll:
/** * 服務員 */ public class Waiter extends Thread { private Dinner d; public Waiter(Dinner d) { super(); this.d = d; } @Override public void run() { synchronized (d) { d.notifyAll(); d.setMainDish("牛扒"); // 至關於上菜 System.out.println(currentThread().getName() + ", waiter notify."); } super.run(); } }
能夠看到日誌:
Thread-0, customer start to wait. Thread-1, customer start to wait. Thread-2, waiter notify. Thread-1, customer end to wait. Thread-1, customer eat the food. Thread-0, customer end to wait. Thread-0, customer start to wait.
Thread-2通知你們後,Thread-0、Thread-1都中止等待了,只不過Thread-1搶到了食物,吃完了,因此Thread-0又得從新等待。
若是各線程等待的條件不同,那麼要用notifyAll,由於用notify只通知到一個線程,而那線程的不知足跳出等待的條件,那麼不就很差了嗎。
好比,有一位富有的客人,要求晚餐的主菜中要有紅酒才知足,與普通客人等待的條件不同,若是服務員只准備了牛扒沒有紅酒,而有用notify只通知了富有的客人,那麼普通的客人就沒被通知到了。
/** * 富有的客人 */ public class WealthyCustomer extends Thread { private Dinner d; public WealthyCustomer(Dinner d) { super(); this.d = d; } @Override public void run() { synchronized (d) { while (d == null || d.getMainDish() == null || !d.getMainDish().contains("紅酒")) { // 富有的客人,要求的主菜要有紅酒 try { System.out.println(currentThread().getName() + ", wealthy customer start to wait."); d.wait(); System.out.println(currentThread().getName() + ", wealthy customer end to wait."); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } d.setMainDish(null); // 至關於把菜吃掉 System.out.println(currentThread().getName() + ", wealthy customer eat the food."); } super.run(); } }
import java.util.concurrent.TimeUnit; public class HowToUse { public static void main(String[] args) { Dinner d = new Dinner(); Customer c1 = new Customer(d); WealthyCustomer c2 = new WealthyCustomer(d); Waiter w = new Waiter(d); c2.start(); // 將WealthyCustomer的線程的先啓動,比較容易看到Waiter通知他 c1.start(); /* 等待一段時間,目的讓Customer線程先啓動 */ try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } w.start(); } }
將Waiter設置成notify,會看到以下日誌:
Thread-1, wealthy customer start to wait. Thread-0, customer start to wait. Thread-2, waiter notify. Thread-1, wealthy customer end to wait. Thread-1, wealthy customer start to wait.
將Waiter設置成notifyAll,日誌以下:
Thread-1, wealthy customer start to wait. Thread-0, customer start to wait. Thread-2, waiter notify. Thread-0, customer end to wait. Thread-0, customer eat the food. Thread-1, wealthy customer end to wait. Thread-1, wealthy customer start to wait.
Thread.sleep(long millis) API doc
《Java編程思想》,機械工業出版社