>关于本文

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

  1. sleep()和wait()的区别。
  2. wait()与notify()、notifyAll()之前互相协调的关系。
  3. 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

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. javascript基础 思维导图2

    来源于:http://www.cnblogs.com/xianyulaodi/p/5886128.html 1.javascript数据类型 2.javascript数组 3.javascript运算 ...

  2. 一行Python

    只要理解了函数式编程,使用神奇的Lambda, 配合列表推导以及复杂一点的判断语句,任何的python 代码都可以转换成一行代码 1.打印九九乘法表 # 打印九九乘法表 print('\n'.join ...

  3. HDU 4302 Holedox Eating (STL + 模拟)

    Holedox Eating Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  4. php 将秒数转换为时间(年、天、小时、分、秒)

    $t=1637544; $d=Sec2Time($t); $d为  0年18天 22小时52分24秒 //将秒数转换为时间(年.天.小时.分.秒) function Sec2Time($time){ ...

  5. appium简明教程(2)——appium的基本概念

    Client/Server Architecture appium的核心其实是一个暴露了一系列REST API的server. 这个server的功能其实很简单:监听一个端口,然后接收由client发 ...

  6. ural 1091. Tmutarakan Exams(容斥)

    http://acm.timus.ru/problem.aspx? space=1&num=1091 从1~s中选出k个数,使得k个数的最大公约数大于1,问这种取法有多少种. (2<=k ...

  7. SharePoint CAML In Action——Part II

    在SharePoint中,相对于Linq to SharePoint而言,CAML是轻量化的.当然缺点也是显而易见的,"Hard Code"有时会让你抓狂.在实际场景中,经常会根据 ...

  8. 【转载,待整理】初学 springmvc整合shiro

    1. shiro认证流程理解 2. 整合过程 http://blog.csdn.net/dawangxiong123/article/details/53020424 http://blog.csdn ...

  9. 【转载】Spring Cache介绍

    原文地址:http://www.cnblogs.com/rollenholt/p/4202631.html 缓存是实际工作中非常常用的一种提高性能的方法, 我们会在许多场景下来使用缓存. 本文通过一个 ...

  10. [转]SpringMVC中使用Interceptor拦截器

    SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理.比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那 ...