了解了在 Java 中如何启动一个线程,并且学习了 Thread 类的 API 以及线程的状态和锁的概念后,接下来我们学习一下线程之间的等待和通知,也就是wait/notify。我们首先思考一个问题,为什么要有wait/notify?

线程和线程之间并不是完全封闭的,可以通过共享变量进行线程间通信。但是多个线程访问同一共享变量时,如果没有同步机制,那么共享变量的值可能会出问题,并不是真正的结果,或者说不是准确的值,出现了运算错误,而我们的wait/notify就是为了解决这一问题的而出现的。

wait/notify 概念

当前线程启动后,调用对象的wait()方法,当前线程释放对象锁,从运行态进入阻塞态,然后进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒,再继续进入运行态,继续执行。

还有notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

线程wait

值得注意的是这两个方法不在Thread对象中,而是在Object中,也就是说所有的java类继承了这两个方法,而java类继承这两个方法的原因是java同步操作的需要,说着这里我们必须对线程同步有所了解。

线程同步

什么是线程同步呢?我们先看看什么是异步,异步其实就是指多个线程同时执行。但在多个线程同时执行的过程中,可能会访问共享资源,此时我们希望确保多个线程在同一时间只能有一个线程访问,此时就称之为线程同步,最简单的就是加锁来实现。

使用实例:

两位老师给学生留下抄单词的作业,最多留下十份作业,两个学生从十分作业中各自拿出一份抄写,当作业不够十份后,老师继续留作业,指导够十份为止,然后等待,不够十份继续留作业。学生一直抄写作业,当把老师留下的作业抄写完毕,等待,然后老师留作业后继续抄写。

public class Student extends Thread {private String name;private LinkedList<Task> tasks;public Student(String name, LinkedList<Task> tasks) {//调用Thread构造方法,设置threadNamesuper(name);this.name = name;this.tasks = tasks;}public void copyWord() throws InterruptedException {String threadName = Thread.currentThread().getName();while (true) {Task task = null;synchronized (tasks) {if (tasks.size() > 0) {task = tasks.removeFirst();sleep(100);tasks.notifyAll();} else {System.out.println(threadName+"开始等待");tasks.wait();System.out.println("学生线程 "+threadName + "线程-" + name + "等待结束");}}if (task != null) {for (int i = 1; i <= task.getLeftCopyCount(); i++) {System.out.println(threadName + "线程-" + name + "抄写" + task.getWordToCopy() + "。已经抄写了" + i + "次");}}}}//重写run方法,完成任务。@Overridepublic void run() {try {copyWord();} catch (InterruptedException e) {e.printStackTrace();}}
}
public class Task {private int leftCopyCount;public int getLeftCopyCount() {return leftCopyCount;}public Task(int leftCopyCount, String wordToCopy) {this.leftCopyCount = leftCopyCount;this.wordToCopy = wordToCopy;}public void setLeftCopyCount(int leftCopyCount) {this.leftCopyCount = leftCopyCount;}public String getWordToCopy() {return wordToCopy;}public void setWordToCopy(String wordToCopy) {this.wordToCopy = wordToCopy;}private String wordToCopy;
}
public class Teacher extends Thread {private String name;private List<String> punishWords = Arrays.asList("internationalization", "hedgehog", "penicillin", "oasis", "nirvana", "miserable");private LinkedList<Task> tasks;private int MAX = 10;public Teacher(String name, LinkedList<Task> tasks) {//调用Thread构造方法,设置threadNamesuper(name);this.name = name;this.tasks = tasks;}public void arrangePunishment() throws InterruptedException {String threadName = Thread.currentThread().getName();while (true) {synchronized (tasks) {if (tasks.size() < MAX) {Task task = new Task(new Random().nextInt(3) + 1, getPunishedWord());tasks.addLast(task);System.out.println(threadName + "留了作业,抄写" + task.getWordToCopy() + " " + task.getLeftCopyCount() + "次");tasks.notifyAll();} else {System.out.println(threadName+"开始等待");tasks.wait();System.out.println("teacher线程 " + threadName + "线程-" + name + "等待结束");}}}}//重写run方法,完成任务。@Overridepublic void run() {try {arrangePunishment();} catch (InterruptedException e) {e.printStackTrace();}}private String getPunishedWord() {return punishWords.get(new Random().nextInt(punishWords.size()));}
}
public class WaitNotifyClient {public static void main(String[] args) {LinkedList<Task> tasks = new LinkedList<>();Student xiaoming = new Student("小明", tasks);xiaoming.start();Student xiaowang = new Student("小王", tasks);xiaowang.start();Teacher lilaoshi = new Teacher("李老师", tasks);lilaoshi.start();Teacher zhanglaoshi = new Teacher("张老师", tasks);zhanglaoshi.start();}
}

运行结果如下:

使用中的注意事项:

1.wait是阻塞当前进程,必须获得锁才能wait,一般配置synchronized关键字使用。反之,如果线程能执行wait,notify/notify等方法,说明线程一定是获得了锁的。

2.当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。

4、wait() 需要被try catch包围,以便发生异常中断也可以使wait等待的线程唤醒。

5、notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程在执行wait方法,那么B线程是无法被唤醒的。

6、notify 和 notifyAll的区别

notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。

【并发编程】线程等待、通知(wait/notify)相关推荐

  1. 超强图文|并发编程【等待/通知机制】就是这个feel~

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If ...

  2. 高并发编程-线程通信_使用wait和notify进行线程间的通信2_多生产者多消费者导致程序假死原因分析

    文章目录 概述 jstack或者可视化工具检测是否死锁(没有) 原因分析 概述 高并发编程-线程通信_使用wait和notify进行线程间的通信 - 遗留问题 我们看到了 应用卡住了 .... 怀疑是 ...

  3. python 线程同步_Python并发编程-线程同步(线程安全)

    Python并发编程-线程同步(线程安全) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 线程同步,线程间协调,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直 ...

  4. 并发编程——线程协作

    并发编程--线程协作 ​ 前面学习了线程,那么并发编程中,如何协调多个线程来开发呢? Semaphore ​ 信号量跟前面将的同步互斥解决方案--信号量是一个东西,这是JDK的信号量实现. 源码分析 ...

  5. 判断线程是否执行完毕_Java并发编程 | 线程核心机制,基础概念扩展

    源码地址:GitHub || GitEE 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效率.下面提供一个 ...

  6. Java 并发编程 -- 线程池源码实战

    一.概述 小编在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写的太简单,只写了一点皮毛,要么就是是晦涩难懂,看完之后几乎都 ...

  7. Java并发编程—线程间协作方式wait()、notify()、notifyAll()和Condition

    原文作者:Matrix海 子 原文地址:Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 目录 一.wait().notify()和notifyA ...

  8. Java并发编程-线程安全基础

    线程安全基础 1.线程安全问题 2.账户取款案例 3.同步代码块synchronized synchronized的理解 java中有三大变量的线程安全问题 在实例方法上使用synchronized ...

  9. C++并发编程线程间共享数据std::future和sd::promise

    线程间共享数据 使用互斥锁实现线程间共享数据 为了避免死锁可以考虑std::lock()或者boost::shared_mutex 要尽量保护更少的数据 同步并发操作 C++标准库提供了一些工具 可以 ...

  10. 并发编程线程通信之管道流

    前言 在并发编程中,需要处理两个问题:线程之间如何通信及线程之间如何同步.通知是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里, ...

最新文章

  1. struts 中的创建Action的三种方法
  2. 第 52 章 Web Server Optimization
  3. python 用itchat会封吗_在python中使用itchat发送微信消息
  4. java循环之后求和代码,Java lambda 循环累加求和代码
  5. apache + phpStudy 配置vue history模式
  6. 210页的《pandas官方文档中文版》.pdf
  7. Mockito单元测试
  8. python ttk style_python – 关于使用ttk.Style()的问题?
  9. 电机学测试题+课后习题+思考题
  10. IJCAI TEXT PAPERS
  11. 云服务器多开账号,怎么用云服务器多开模拟器
  12. 那些年,Github上的干货!
  13. 所有iOS设备的屏幕分辨率
  14. 《零售时代4.0》读后感
  15. 海南大学信号与系统838报考高频问题整理(五)
  16. [经验分享] 覃超直播课学习笔记
  17. day62-github与gitlab的使用
  18. 2017年科技界十大新闻,你都知道吗?
  19. Spring Cloud Discovery——Apache Zookeeper Discovery
  20. sai1的笔刷安装到sai2中

热门文章

  1. linux系统内存执行elf的多种方式
  2. installer,source,binary,archive 版本区别
  3. linux c 宏定义 #define _GNU_SOURCE 含义
  4. /lib64/libc.so.6: version `GLIBC_2.14' not found问题
  5. Android之 AndroidManifest.xml 文件解析
  6. 面试题目集锦 -- 排序算法
  7. 对C语言中递归算法的分析
  8. Eclipse Theme
  9. Android开发--Wifi的操作
  10. linux 线程 进程经典文章