>关于本文

本文介绍sleep()、wait()、notify()、notifyAll()方法,主要要理解:

  1. sleep()和wait()的区别。
  2. wait()与notify()、notifyAll()之前互相协调的关系。
  3. notify()与notifyAll()的区别。

> Thread.sleep(long),睡眠指定时间

此方法是让线程睡眠指定时间,不释放锁(睡觉,当然要上锁,这个还用说么)。

此方法我貌似很少用,又似乎很常用。因为,在正式代码中我很少用到,而在测试代码中,却又经常用来模拟某某业务需时几秒的阻塞。

  1. public class Sleep {
  2.  
  3. public static void main(String[] args) {
  4.  
  5. System.out.println("Start...");
  6.  
  7. try {
  8. Thread.sleep(5000);
  9. } catch (InterruptedException e) {
  10. // TODO Auto-generated catch block
  11. e.printStackTrace();
  12. }
  13.  
  14. System.out.println("End...");
  15.  
  16. }
  17.  
  18. }

一般,还有个更易读的写法,一样的效果

  1. import java.util.concurrent.TimeUnit;
  2.  
  3. public class Sleep {
  4.  
  5. public static void main(String[] args) {
  6.  
  7. System.out.println("Start...");
  8.  
  9. try {
  10. TimeUnit.SECONDS.sleep(5);
  11. } catch (InterruptedException e) {
  12. // TODO Auto-generated catch block
  13. e.printStackTrace();
  14. }
  15.  
  16. System.out.println("End...");
  17.  
  18. }
  19.  
  20. }

> Object.wait(),等待(条件)

线程转到等待状态,释放锁。(既然等待,当然得释放锁了,我们在等待、迎接贵宾时,也是敞开着大门的,哈哈)

在持有锁的情况下才能调用此方法,通常搭配外层的循环以判断是否继续等待。

wait方法可以执行时间,也可不,由notify方法唤醒。

晚餐、客人、服务员的例子

  1. /**
  2. * 晚餐
  3. */
  4. public class Dinner {
  5.  
  6. private String mainDish; // 主菜
  7.  
  8. public String getMainDish() {
  9. return mainDish;
  10. }
  11.  
  12. public void setMainDish(String mainDish) {
  13. this.mainDish = mainDish;
  14. }
  15.  
  16. }
  1. /**
  2. * 客人
  3. */
  4. public class Customer extends Thread {
  5.  
  6. private Dinner d;
  7.  
  8. public Customer(Dinner d) {
  9. super();
  10. this.d = d;
  11. }
  12.  
  13. @Override
  14. public void run() {
  15. synchronized (d) {
  16. while (d == null || d.getMainDish() == null) {
  17. try {
  18. System.out.println(currentThread().getName() + ", customer start to wait.");
  19. d.wait();
  20. System.out.println(currentThread().getName() + ", customer end to wait.");
  21. } catch (InterruptedException e) {
  22. // TODO Auto-generated catch block
  23. e.printStackTrace();
  24. }
  25. }
  26.  
  27. d.setMainDish(null); // 相当于把菜吃掉
  28. System.out.println(currentThread().getName() + ", customer eat the food.");
  29. }
  30. super.run();
  31. }
  32.  
  33. }
  1. /**
  2. * 服务员
  3. */
  4. public class Waiter extends Thread {
  5.  
  6. private Dinner d;
  7.  
  8. public Waiter(Dinner d) {
  9. super();
  10. this.d = d;
  11. }
  12.  
  13. @Override
  14. public void run() {
  15. synchronized (d) {
  16. d.notify();
  17. d.setMainDish("牛扒"); // 相当于上菜
  18. System.out.println(currentThread().getName() + ", waiter notify.");
  19. }
  20. super.run();
  21. }
  22.  
  23. }
  1. import java.util.concurrent.TimeUnit;
  2.  
  3. public class HowToUse {
  4.  
  5. public static void main(String[] args) {
  6. Dinner d = new Dinner();
  7. Customer c = new Customer(d);
  8. Waiter w = new Waiter(d);
  9.  
  10. c.start();
  11.  
  12. /* 等待一段时间,目的让Customer线程先启动 */
  13. try {
  14. TimeUnit.MILLISECONDS.sleep(500);
  15. } catch (InterruptedException e) {
  16. // TODO Auto-generated catch block
  17. e.printStackTrace();
  18. }
  19.  
  20. w.start();
  21. }
  22.  
  23. }

日志:

  1. Thread-0, customer start to wait.
  2. Thread-1, waiter notify.
  3. Thread-0, customer end to wait.
  4. Thread-0, customer eat the food.

当然,也可执行时长停止等待了:

  1. public class Wait {
  2.  
  3. public static void main(String[] args) {
  4.  
  5. System.out.println("Start...");
  6.  
  7. String s = "";
  8. synchronized (s) {
  9. try {
  10. s.wait(5000);
  11. } catch (InterruptedException e) {
  12. // TODO Auto-generated catch block
  13. e.printStackTrace();
  14. }
  15. }
  16.  
  17. System.out.println("End...");
  18.  
  19. }
  20.  
  21. }

> Object.notify()和Object.notifyAll(),唤醒等待的线程

一个是通知一个线程(通知哪一个线程是不确定的哦),另一个是唤醒全部线程。

将HowToUse方法修改下:

  1. import java.util.concurrent.TimeUnit;
  2.  
  3. public class HowToUse {
  4.  
  5. public static void main(String[] args) {
  6. Dinner d = new Dinner();
  7. Customer c1 = new Customer(d);
  8. Customer c2 = new Customer(d);
  9. Waiter w = new Waiter(d);
  10.  
  11. c1.start();
  12. c2.start();
  13.  
  14. /* 等待一段时间,目的让Customer线程先启动 */
  15. try {
  16. TimeUnit.MILLISECONDS.sleep(500);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21.  
  22. w.start();
  23. }
  24.  
  25. }

就可以看到:

  1. 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:

  1. /**
  2. * 服务员
  3. */
  4. public class Waiter extends Thread {
  5.  
  6. private Dinner d;
  7.  
  8. public Waiter(Dinner d) {
  9. super();
  10. this.d = d;
  11. }
  12.  
  13. @Override
  14. public void run() {
  15. synchronized (d) {
  16. d.notifyAll();
  17. d.setMainDish("牛扒"); // 相当于上菜
  18. System.out.println(currentThread().getName() + ", waiter notify.");
  19. }
  20. super.run();
  21. }
  22.  
  23. }

可以看到日志:

  1. Thread-0, customer start to wait.
  2. Thread-1, customer start to wait.
  3. Thread-2, waiter notify.
  4. Thread-1, customer end to wait.
  5. Thread-1, customer eat the food.
  6. Thread-0, customer end to wait.
  7. Thread-0, customer start to wait.

Thread-2通知大家后,Thread-0、Thread-1都停止等待了,只不过Thread-1抢到了食物,吃完了,所以Thread-0又得重新等待。

> 什么时候用notify,什么时候用notifyAll?

如果各线程等待的条件不一样,那么要用notifyAll,因为用notify只通知到一个线程,而那线程的不满足跳出等待的条件,那么不就不好了吗。

比如,有一位富有的客人,要求晚餐的主菜中要有红酒才满足,与普通客人等待的条件不一样,如果服务员只准备了牛扒没有红酒,而有用notify只通知了富有的客人,那么普通的客人就没被通知到了。

  1. /**
  2. * 富有的客人
  3. */
  4. public class WealthyCustomer extends Thread {
  5.  
  6. private Dinner d;
  7.  
  8. public WealthyCustomer(Dinner d) {
  9. super();
  10. this.d = d;
  11. }
  12.  
  13. @Override
  14. public void run() {
  15. synchronized (d) {
  16. while (d == null || d.getMainDish() == null || !d.getMainDish().contains("红酒")) { // 富有的客人,要求的主菜要有红酒
  17. try {
  18. System.out.println(currentThread().getName() + ", wealthy customer start to wait.");
  19. d.wait();
  20. System.out.println(currentThread().getName() + ", wealthy customer end to wait.");
  21. } catch (InterruptedException e) {
  22. // TODO Auto-generated catch block
  23. e.printStackTrace();
  24. }
  25. }
  26.  
  27. d.setMainDish(null); // 相当于把菜吃掉
  28. System.out.println(currentThread().getName() + ", wealthy customer eat the food.");
  29. }
  30. super.run();
  31. }
  32.  
  33. }
  1. import java.util.concurrent.TimeUnit;
  2.  
  3. public class HowToUse {
  4.  
  5. public static void main(String[] args) {
  6. Dinner d = new Dinner();
  7. Customer c1 = new Customer(d);
  8. WealthyCustomer c2 = new WealthyCustomer(d);
  9. Waiter w = new Waiter(d);
  10.  
  11. c2.start(); // 将WealthyCustomer的线程的先启动,比较容易看到Waiter通知他
  12. c1.start();
  13.  
  14. /* 等待一段时间,目的让Customer线程先启动 */
  15. try {
  16. TimeUnit.MILLISECONDS.sleep(500);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21.  
  22. w.start();
  23. }
  24.  
  25. }

将Waiter设置成notify,会看到如下日志:

  1. Thread-1, wealthy customer start to wait.
  2. Thread-0, customer start to wait.
  3. Thread-2, waiter notify.
  4. Thread-1, wealthy customer end to wait.
  5. Thread-1, wealthy customer start to wait.

将Waiter设置成notifyAll,日志如下:

  1. Thread-1, wealthy customer start to wait.
  2. Thread-0, customer start to wait.
  3. Thread-2, waiter notify.
  4. Thread-0, customer end to wait.
  5. Thread-0, customer eat the food.
  6. Thread-1, wealthy customer end to wait.
  7. Thread-1, wealthy customer start to wait.

> 参考的优秀文章

Thread.sleep(long millis) API doc

wait(long timeout) API doc

notify() API doc

notifyAll() API doc

《Java编程思想》,机械工业出版社

【Java Concurrency】sleep()、wait()、notify()、notifyAll()的用法与区别的更多相关文章

  1. java中的wait(),notify(),notifyAll(),synchronized方法

    wait(),notify(),notifyAll()三个方法不是Thread的方法,而是Object的方法.意味着所有对象都有这三个方法,因为每个对象都有锁,所以自然也都有操作锁的方法了.这三个方法 ...

  2. Java多线程的wait(),notify(),notifyAll()

    在多线程的情况下.因为多个线程与存储空间共享相同的过程,同时带来的便利.它也带来了访问冲突这个严重的问题. Java语言提供了一种特殊的机制来解决这类冲突,避免同一数据对象由多个线程在同一时间访问. ...

  3. Java多线程:wait(),notify(),notifyAll()

    1. wait(),notify(),notifyAll() 2. wait() 2.1. wait() 2.2. wait(long timeout) 2.3. wait(long timeout, ...

  4. 线程中wait/notify/notifyAll的用法

    前言 多线程时,最关注的就是线程同步,线程间的同步一般用锁来实现,常见的锁就是synchronized和lock.用了synchronized,就不得不提到wait/notify/notifyAll. ...

  5. java 并发——理解 wait / notify / notifyAll

    一.前言 前情简介: java 并发--内置锁 java 并发--线程 java 面试是否有被问到过,sleep 和 wait 方法的区别,关于这个问题其实不用多说,大多数人都能回答出最主要的两点区别 ...

  6. java 多线程(wait/notify/notifyall)

    package com.example; public class App { /* wait\notify\notifyAll 都属于object的内置方法 * wait: 持有该对象的线程把该对象 ...

  7. Java多线程--wait(),notify(),notifyAll()的用法

    忙等待没有对运行等待线程的 CPU 进行有效的利用(而且忙等待消耗cpu过于恐怖,请慎用),除非平均等待时间非常短.否则,让等待线程进入睡眠或者非运行状态更为明智,直到它接收到它等待的信号. Java ...

  8. 通过两个小栗子来说说Java的sleep、wait、notify、notifyAll的用法

    线程是计算程序运行的最小载体,由于单个单核CPU的硬件水平发展到了一定的瓶颈期,因此就出现了多核多CPU的情况,直接就导致程序员多线程编程的复杂.由此可见线程对于高性能开发的重要性. 那么线程在计算机 ...

  9. java 多线程之wait(),notify,notifyAll(),yield()

    wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能.因为都个对像都 ...

随机推荐

  1. 【TP3.2.X】linux环境下TP3.2.X的各个目录权限

    1.将整个项目文件 所属设置成www:www,单个文件夹 755 2.Application 单文件夹是755 3.Runtime.Public .Uploads  均是  -R 777

  2. 【js+jquery】通用、简单的JS 提示框

    1.该插件不需要依赖 jquery,仅仅使用了原生js 2.简单.通用.可自定义修改样式.支持等待N秒消失.支持消失后跳转其他url , 功能还是比较完善的. 3.不废话,上代码: (我存放的位置在 ...

  3. uitableview做九宫格

    1:创建实体 #import <Foundation/Foundation.h> @interface Shop : NSObject @property (nonatomic, copy ...

  4. Geocoding java调用百度地图API v2.0 图文 实例( 解决102错误)

    如何使用? 第一步:申请ak(即获取密钥),若无百度账号则首先需要注册百度账号. 第二步,拼写发送http请求的url,注意需使用第一步申请的ak. 第三步,接收http请求返回的数据(支持json和 ...

  5. Java 过滤特殊字符的 正则表达式

    Java正则表达式学习: 因为正则表达式是一个很庞杂的体系,此例仅举些入门的概念,更多的请参阅相关书籍及自行摸索. \\ 反斜杠 \t 间隔 ('\u0009') \n 换行 ('\u000A') \ ...

  6. Android之listview运用(美团美食列表)

    首先我们将listview简单实现,有图形,有文字:效果如图 之前我们完成了一个较为简单的listview视图列表,但是生活中我们往往碰到的 是更为复杂列表,有图像有评分标准,不如我们来试一试手,做一 ...

  7. C# WinForm开发系列 - DataGrid/DataGridView

    在WinForm开发中,DataGrid/DataGridView被广泛使用于绑定数据库中数据进行呈现.整理一些关于DataGrid/DataGridView使用的文章,涉及DataGrid/Data ...

  8. 【Android开发】之Fragment生命周期

    上一篇博客我们讲到了,Fragment的基本使用,相信大家都已经了解怎么去使用了.如果还有不懂得同学可以去看一下,传送门.现在我们来讲解一下Fragment的生命周期. 一.Fragment的事务 再 ...

  9. mysql升级5.5

    对付Linux的问题,其实很多都是权限问题,细心想一下即可. centos6.4默认装的是mysql5.1,使用 yum update 也update不了.google了一下,找到个yum安装的方法: ...

  10. 简单获取cpu使用率,以及后台运行的问题

    做了一个运维平台,有一个功能定时执行一个脚本,获取cpu使用率和内存使用情况到监控平台. 获取cpu使用率使用的是top中的信息.直接运行没有问题.通过nohup xxx.sh & 之后获取不 ...