协作基础(wait/notify)

Java的根父类是Object,Java在Object类而非Thread类中,定义了一些线程协作的基本方法,使得每个对象都可以调用这些方法,这些方法有两类,一类是wait,另一类是notify。

wait方法主要有两个:

public final void wait() throws InterruptedException
public final native void wait(long timeout) throws InterruptedException;

一个带时间参数,单位是毫秒,表示最多等待这么长时间,参数为0表示无限期等待。一个不带时间参数,表示无限期等待,实际就是调用wait(0)。在等待期间都可以被中断,如果被中断,会抛出InterruptedException。

wait实际上做了什么呢?每个对象都有一把锁和一个锁等待队列,一个线程在进入synchronized代码块时,会尝试获取锁,获取不到的话会把当前线程加入等待队列中。其实,除了用于锁的等待队列,每个对象还有另一个等待队列,表示条件队列,该队列用于线程间的协作。调用wait就会把当前线程放到条件队列上并阻塞,表示当前线程执行不下去了,它需要等待一个条件,这个条件它自己改变不了,需要其他线程改变。当其他线程改变了条件后,应该调用Object的notify方法:

public final native void notify();
public final native void notifyAll();

notify做的事情就是从条件队列中选一个线程,将其从队列中移除并唤醒,notifyAll和notify的区别是,它会移除条件队列中所有的线程并全部唤醒。

wait/notify方法只能在synchronized代码块内被调用,如果调用wait/notify方法时,当前线程没有持有对象锁,会抛出异常java.lang.IllegalMonitorStateException。

wait的具体过程是:

  1. 把当前线程放入条件等待队列,释放对象锁,阻塞等待,线程状态变为WAITING或TIMED_WAITING
  2. 等待时间到或被其他线程调用notify/notifyAll从条件队列中移除,这时,要重新竞争对象锁
    • 如果能够获得锁,线程状态变为RUNNABLE,并从wait调用中返回
    • 否则,该线程加入对象锁等待队列,线程状态变为BLOCKED,只有在获得锁后才会从wait调用中返回

线程从wait调用中返回后,不代表其等待的条件就一定成立了,它需要重新检查其等待的条件,一般的调用模式是:

synchronized (obj) {while (条件不成立)obj.wait();... // 条件满足后的操作
}

生产者/消费者模式

下面来看一个生产者和消费者的例子:

/*** @author 沉默哥* */
public class MyProducerConsumerDemo {static class GoodsQueue {private int size;private Queue<String> que = new ArrayDeque<String>();public GoodsQueue(int size) {// 维护一个有界队列,传入队列的最大容量super();this.size = size;}public synchronized void put(String e) throws InterruptedException {while (que.size() == size) {System.out.println("队列已满,生产者等待");wait();}que.add(e);System.out.println("生产者生产:" + e);notify();}public synchronized String take() throws InterruptedException {while (que.size() == 0) {System.out.println("队列为空,消费者等待");wait();}String e = que.poll();System.out.println("消费者消费" + e);notify();return e;}}static class Producer extends Thread {GoodsQueue que;Random rad = new Random();public Producer(GoodsQueue que) {super();this.que = que;}@Overridepublic void run() {int i = 0;try {while (true) {String e = String.valueOf(i);que.put(e);i++;Thread.sleep(rad.nextInt(1000));// 生产者休息准备下一次生产
        }} catch (InterruptedException e1) {}}}static class Consumer extends Thread {GoodsQueue que;Random rad = new Random();public Consumer(GoodsQueue que) {super();this.que = que;}@Overridepublic void run() {try {while (true) {que.take();Thread.sleep(rad.nextInt(1000));// 消费者休息准备下一次消费
        }} catch (InterruptedException e) {}}}public static void main(String[] args) throws InterruptedException {GoodsQueue que = new GoodsQueue(1);Producer pro = new Producer(que);Consumer con = new Consumer(que);con.start();Thread.sleep(500);pro.start();}
}

转载于:https://www.cnblogs.com/JackPn/p/9426366.html

线程的基本协作和生产者消费者相关推荐

  1. 菜鸟学习笔记:Java提升篇8(线程2——线程的基本信息、线程安全、死锁、生产者消费者模式、任务调度)

    菜鸟学习笔记:Java提升篇8(线程2--线程的基本信息.线程安全.死锁.生产者消费者模式.任务调度) 线程的基本信息 线程同步 线程安全 死锁 生产者消费者模式 任务调度(了解) 线程的基本信息 J ...

  2. Java 多线程(二)线程间的通信应用--生产者消费者(未完)

    对于多个生产者和消费者. 为什么要定义while判断标记. 原因:让被唤醒的线程再一次判断标记. 为什么定义notifyAll, 因为需要唤醒对方线程. 因为只用notify,容易出现只唤醒本方线程的 ...

  3. 并发协作模型“生产者/消费者模式“

    java提供了几个方法解决线程之间的通信问题 方法名 作用 wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁 wait(long timeout) 指定等待的毫秒数 not ...

  4. java-线程安全问题,线程实现线程同步,线程状态,等待唤醒机制,生产者消费者模型

    目录标题 解决线程安全问题手段:线程同步 实现同步操作步骤 1.同步代码块 2.同步方法 静态同步方法 3.Lock接口的锁机制 线程状态 生产者消费者模型:等待唤醒 等待唤醒案例 线程安全问题是不能 ...

  5. 线程协作与生产者消费者问题

    目录 线程协作 生产者和消费者问题 案例-面包店 说明 代码实现 开发面包柜(共享数据资源) 测试代码 线程协作 在多线程开发中,为避免共享资源数据错误,常使用互斥(synchronized)机制实现 ...

  6. Java20-day11【实现多线程(进程、线程-调度-控制-生命周期)、线程同步(同步代码块、线程安全、Lock)、生产者消费者(模式概述、案例)】

    视频+资料[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg   提取码:zjxs] Java基础--学习笔记(零起点打开java世界的大门)--博 ...

  7. [19/04/11-星期四] 多线程_并发协作(生产者/消费者模式_2种解决方案(管程法和信号灯法))...

    一.概念 多线程环境下,我们经常需要多个线程的并发和协作.这个时候,就需要了解一个重要的多线程并发协作模型"生产者/消费者模式". Ø 什么是生产者? 生产者指的是负责生产数据的模 ...

  8. Linux系统编程---17(条件变量及其函数,生产者消费者条件变量模型,生产者与消费者模型(线程安全队列),条件变量优点,信号量及其主要函数,信号量与条件变量的区别,)

    条件变量 条件变量本身不是锁!但它也可以造成线程阻塞.通常与互斥锁配合使用.给多线程提供一个会合的场所. 主要应用函数: pthread_cond_init 函数 pthread_cond_destr ...

  9. 多线程终极模式:生产者-消费者模式

    多线程de小事情 导航不迷路: 程序.进程以及线程的爱恨情仇 最简单实现多线程的方法(Thread) 简单易懂的多线程(通过实现Runnable接口实现多线程) 常用获取线程基本信息的方法(新手专属) ...

最新文章

  1. 你必须学会的Git入门基本操作
  2. linux的安装组和管理组的区别吗,Linux用户和组管理
  3. Python 依赖库
  4. 专门跑顺风车真的挣钱吗?
  5. 使用Windows 8 的“任务计划”令HydraVision更加精彩
  6. 除了微软默认的ppt服务器外,微软如此解释这一新政。据了解,除了MSN与Skype有很多类似功能之外.ppt...
  7. 【现代通信原理笔记】2 无线信道的传播特征
  8. 二维空间的抛物型偏微分方程基本解法——ADI与紧ADI方法
  9. ubuntu9.10 添加bones7456源
  10. 最新H5游戏小游戏集成系统400多款趣味游戏
  11. 性能测试面试题(测试框架总结)史上最全面试题
  12. PDF文件如何压缩大小
  13. Android基于G-Sensor的计步算法
  14. wordpress php.ini在哪,wordpress插件文件目录在哪
  15. 【注册测绘师】攻略 1.连续运行基准参考站(CORS)系统干货
  16. 经济基础知识(初级)【4】
  17. E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarly unavailable)
  18. win10计算机未连接到网络适配器,Win10网络适配器显示未连接怎么解决 - 系统之家...
  19. CQUPT第十三届ACM网赛 H. 活动图 题解
  20. 默纳克修改楼层服务器菜单,默纳克系统小键盘各菜单功能详述

热门文章

  1. 计算机软件技能高考好考么,湖北技能高考:上不了本科,优先考虑这4所一档高职...
  2. 零基础可以学习java吗_零基础真的可以学习java吗?
  3. plsql一直正在编译_使用plsql/devlop编译过程hang住案列小结
  4. php mysql 测试页_mysql+php分页类(已测)
  5. python水平_python水平
  6. 服务器实际显示内存,服务器实际显示内存大小
  7. 【算法竞赛学习】学术前沿趋势-论文作者统计
  8. linux ping库函数,Linux 常用基本命令 ping ifconfig
  9. 『ACM-算法-离散化』信息竞赛进阶指南--离散化
  10. Codeforces 1291 Round #616 (Div. 2) B