【并发编程】线程等待、通知(wait/notify)
了解了在 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)相关推荐
- 超强图文|并发编程【等待/通知机制】就是这个feel~
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If ...
- 高并发编程-线程通信_使用wait和notify进行线程间的通信2_多生产者多消费者导致程序假死原因分析
文章目录 概述 jstack或者可视化工具检测是否死锁(没有) 原因分析 概述 高并发编程-线程通信_使用wait和notify进行线程间的通信 - 遗留问题 我们看到了 应用卡住了 .... 怀疑是 ...
- python 线程同步_Python并发编程-线程同步(线程安全)
Python并发编程-线程同步(线程安全) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 线程同步,线程间协调,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直 ...
- 并发编程——线程协作
并发编程--线程协作 前面学习了线程,那么并发编程中,如何协调多个线程来开发呢? Semaphore 信号量跟前面将的同步互斥解决方案--信号量是一个东西,这是JDK的信号量实现. 源码分析 ...
- 判断线程是否执行完毕_Java并发编程 | 线程核心机制,基础概念扩展
源码地址:GitHub || GitEE 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效率.下面提供一个 ...
- Java 并发编程 -- 线程池源码实战
一.概述 小编在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写的太简单,只写了一点皮毛,要么就是是晦涩难懂,看完之后几乎都 ...
- Java并发编程—线程间协作方式wait()、notify()、notifyAll()和Condition
原文作者:Matrix海 子 原文地址:Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 目录 一.wait().notify()和notifyA ...
- Java并发编程-线程安全基础
线程安全基础 1.线程安全问题 2.账户取款案例 3.同步代码块synchronized synchronized的理解 java中有三大变量的线程安全问题 在实例方法上使用synchronized ...
- C++并发编程线程间共享数据std::future和sd::promise
线程间共享数据 使用互斥锁实现线程间共享数据 为了避免死锁可以考虑std::lock()或者boost::shared_mutex 要尽量保护更少的数据 同步并发操作 C++标准库提供了一些工具 可以 ...
- 并发编程线程通信之管道流
前言 在并发编程中,需要处理两个问题:线程之间如何通信及线程之间如何同步.通知是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里, ...
最新文章
- struts 中的创建Action的三种方法
- 第 52 章 Web Server Optimization
- python 用itchat会封吗_在python中使用itchat发送微信消息
- java循环之后求和代码,Java lambda 循环累加求和代码
- apache + phpStudy 配置vue history模式
- 210页的《pandas官方文档中文版》.pdf
- Mockito单元测试
- python ttk style_python – 关于使用ttk.Style()的问题?
- 电机学测试题+课后习题+思考题
- IJCAI TEXT PAPERS
- 云服务器多开账号,怎么用云服务器多开模拟器
- 那些年,Github上的干货!
- 所有iOS设备的屏幕分辨率
- 《零售时代4.0》读后感
- 海南大学信号与系统838报考高频问题整理(五)
- [经验分享] 覃超直播课学习笔记
- day62-github与gitlab的使用
- 2017年科技界十大新闻,你都知道吗?
- Spring Cloud Discovery——Apache Zookeeper Discovery
- sai1的笔刷安装到sai2中
热门文章
- linux系统内存执行elf的多种方式
- installer,source,binary,archive 版本区别
- linux c 宏定义 #define _GNU_SOURCE 含义
- /lib64/libc.so.6: version `GLIBC_2.14' not found问题
- Android之 AndroidManifest.xml 文件解析
- 面试题目集锦 -- 排序算法
- 对C语言中递归算法的分析
- Eclipse Theme
- Android开发--Wifi的操作
- linux 线程 进程经典文章