wait 和 notify 简介

wait 和 notify 均为 Object 的方法:

  • Object.wait() —— 暂停一个线程
  • Object.notify() —— 唤醒一个线程

从以上的定义中,我们可以了解到以下事实:

  • 想要使用这两个方法,我们需要先有一个对象 Object。
  • 在多个线程之间,我们可以通过调用同一个对象的wait()notify()来实现不同的线程间的可见。

对象控制权(monitor)

在使用 wait 和 notify 之前,我们需要先了解对象的控制权(monitor)。在 Java 中任何一个时刻,对象的控制权只能被一个线程拥有。如何理解控制权呢?请先看下面的简单代码:

public class ThreadTest {
public static void main(String[] args) {
Object object = new Object();
new Thread(new Runnable() {
@Override
public void run() {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}

  

直接执行,我们将会得到以下异常:

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.xiangyu.demo.ThreadTest$1.run(ThreadTest.java:10)
at java.lang.Thread.run(Thread.java:748)

  

出错的代码在:object.wait();。这里我们需要了解以下事实:

  • 无论是执行对象的 wait、notify 还是 notifyAll 方法,必须保证当前运行的线程取得了该对象的控制权(monitor)
  • 如果在没有控制权的线程里执行对象的以上三种方法,就会报 java.lang.IllegalMonitorStateException 异常。
  • JVM 基于多线程,默认情况下不能保证运行时线程的时序性

在上面的示例代码中,我们 new 了一个 Thread,但是对象 object 的控制权仍在主线程里。所以会报 java.lang.IllegalMonitorStateException 。

我们可以通过同步锁来获得对象控制权,例如:synchronized 代码块。对以上的示例代码做改造:

public class ThreadTest {
public static void main(String[] args) {
Object object = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (object){ // 修改处
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}

再次执行,代码不再报错。

我们可以得到以下结论:

  • 调用对象的wait()notify()方法,需要先取得对象的控制权
  • 可以使用synchronized (object)来取得对于 object 对象的控制权

解题

了解了对象控制权之后,我们就可以正常地使用 notify 和 wait 了,下面给出我的解题方法,供参考。

public class ThreadTest {
private final Object flag = new Object(); public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
ThreadA threadA = threadTest.new ThreadA();
threadA.start();
ThreadB threadB = threadTest.new ThreadB();
threadB.start();
} class ThreadA extends Thread {
@Override
public void run() {
synchronized (flag) {
for (int i = 0; i <= 100; i += 2) {
flag.notify();
System.out.println(i);
try {
flag.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }
} class ThreadB extends Thread {
@Override
public void run() {
synchronized (flag) {
for (int i = 1; i < 100; i += 2) {
flag.notify();
System.out.println(i);
try {
flag.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}

发散:notify()notifyAll()

这两个方法均为 native 方法,在JDK 1.8 中的关于notify()的JavaDoc如下:

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened.

译为:

唤醒此 object 控制权下的一个处于 wait 状态的线程。若有多个线程处于此 object 控制权下的 wait 状态,只有一个会被唤醒。

也就是说,如果有多个线程在 wait 状态,我们并不知道哪个线程会被唤醒。

在JDK 1.8 中的关于notifyAll()的JavaDoc如下:

Wakes up all threads that are waiting on this object's monitor.

译为:

唤醒所有处于此 object 控制权下的 wait 状态的线程。

所以,我们需要根据实际的业务场景来考虑如何使用。

原文链接:https://segmentfault.com/a/1190000018096174?utm_source=tag-newest

参考地址:https://mp.weixin.qq.com/s/WoQtQSDygBwC_lJyOhf1EQ

Java 多线程编程之:notify 和 wait 用法的更多相关文章

  1. java多线程编程之synchronized

    synchronized是用来解决多线程情况下的线程安全问题的,它可以修饰方法也可以修饰语句块 , 那么什么情况下是线程安全和线程不安全呢 ? 方法内的变量是线程安全的 , 类的实例变量是非线程安全的 ...

  2. Java 多线程编程之九:使用 Executors 和 ThreadPoolExecutor 实现的 Java 线程池的例子

    线程池用来管理工作线程的数量,它持有一个等待被执行的线程的队列.         java.util.concurrent.Executors 提供了 java.util.concurrent.Exe ...

  3. iOS多线程编程之GCD的常见用法(转载)

    一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) withObject:nil aft ...

  4. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  5. [转] iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用

    介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首 ...

  6. [转] iOS多线程编程之NSOperation和NSOperationQueue的使用

    <iOS多线程编程之NSThread的使用> 介绍三种多线程编程和NSThread的使用,这篇介绍NSOperation的使用. 使用 NSOperation的方式有两种, 一种是用定义好 ...

  7. 深入浅出Cocoa多线程编程之 block 与 dispatch quene

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

  8. iOS多线程编程之NSOperation和NSOperationQueue的使用

    前一篇 <iOS多线程编程之NSThread的使用> 介绍三种多线程编程和NSThread的使用,这篇介绍NSOperation的使用. 使用 NSOperation的方式有两种, 一种是 ...

  9. [Cocoa]深入浅出Cocoa多线程编程之 block 与 dispatch quene

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

随机推荐

  1. redis主从+keepalived实现高可用技术

    Redis是我们当下比较流行使用的非关系数据库,可支持多样化的数据类型,多线程高并发支持,redis运行在内存拥有更快的读写.因为redis的表现如此出色,如何能保障redis在运行中能够应对宕机故障 ...

  2. DevOps之持续集成Pipeline(一)

    一.Pipeline介绍     Jenkins2.0中最大的一个特性就是Pipeline,实际使用中Pipeline已经超越了我们对jenkins本身的理解,可能在之前我们大多数把Jenkins当做 ...

  3. ef复杂模型添加

    模型浏览器 函数导入-添加存储过程名称 添加复杂实体.复杂实体可以手动在xml里面创建.在complextype里面

  4. grpc:gRPC Concepts

    本文介绍一些主要的gRPC概念. 服务定义 gRPC支持4种方法: 1.Unary RPCs where the client sends a single request to the server ...

  5. guava缓存第一篇

    guava缓存主要有2个接口,Cache和LoadingCache. Cache,全类名是com.google.common.cache.Cache,支持泛型.Cache<K, V>. L ...

  6. jquery odd选择器 语法

    jquery odd选择器 语法 作用::odd 选择器选取每个带有奇数 index 值的元素(比如 1.3.5).index 值从 0 开始,所有第一个元素是偶数 (0).最常见的用法:与其他元素/ ...

  7. python 输出一个随机数

    题目:输出一个随机数. 程序分析:使用 random 模块. #!/user/bin/env python #coding:utf-8 import random print random.rando ...

  8. JPA学习(三、JPA_API)

    框架学习之JPA(三) JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中 ...

  9. 解决html5大文件断点续传

    一.概述 所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载.在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了.一般断点下载时才用到Range和Content- ...

  10. Vue(核心思想)

    1.Es6语法普及 let和var的区别: var:定义变量时,在全局范围内都有效;所以在变量没有声明之前就能使用,值为undefined, 称为变量提升; let:声明的变量一定要在声明后使用,而且 ...