>关于本文
本文介绍sleep()、wait()、notify()、notifyAll()方法,主要要理解:
- sleep()和wait()的区别。
- wait()与notify()、notifyAll()之前互相协调的关系。
- notify()与notifyAll()的区别。
> Thread.sleep(long),睡眠指定时间
此方法是让线程睡眠指定时间,不释放锁(睡觉,当然要上锁,这个还用说么)。
此方法我貌似很少用,又似乎很常用。因为,在正式代码中我很少用到,而在测试代码中,却又经常用来模拟某某业务需时几秒的阻塞。
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..."); } }
一般,还有个更易读的写法,一样的效果
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..."); } }
> Object.wait(),等待(条件)
线程转到等待状态,释放锁。(既然等待,当然得释放锁了,我们在等待、迎接贵宾时,也是敞开着大门的,哈哈)
在持有锁的情况下才能调用此方法,通常搭配外层的循环以判断是否继续等待。
wait方法可以执行时间,也可不,由notify方法唤醒。
晚餐、客人、服务员的例子
/** * 晚餐 */ 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(); } }
日志:
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..."); } }
> Object.notify()和Object.notifyAll(),唤醒等待的线程
一个是通知一个线程(通知哪一个线程是不确定的哦),另一个是唤醒全部线程。
将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又得重新等待。
> 什么时候用notify,什么时候用notifyAll?
如果各线程等待的条件不一样,那么要用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编程思想》,机械工业出版社