上一篇讲述了线程的互斥(同步),但是在很多情况下,仅仅同步是不够的,还需要线程与线程协作(通信),生产者/消费者问题是一个经典的线程同步以及通信的案例。该问题描述了两个共享固定大小缓冲区的线程,即所谓的“生产者”和“消费者”在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者,通常采用线程间通信的方法解决该问题。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。本文讲述了JDK5之前传统线程的通信方式,更高级的通信方式可参见Java线程(九):Condition-线程通信更高效的方式和Java线程(篇外篇):阻塞队列BlockingQueue。

假设有这样一种情况,有一个盘子,盘子里只能放一个鸡蛋,A线程专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B线程专门从盘子里取鸡蛋,如果盘子里没鸡蛋,则一直等到盘子里有鸡蛋。这里盘子是一个互斥区,每次放鸡蛋是互斥的,每次取鸡蛋也是互斥的,A线程放鸡蛋,如果这时B线程要取鸡蛋,由于A没有释放锁,B线程处于等待状态,进入阻塞队列,放鸡蛋之后,要通知B线程取鸡蛋,B线程进入就绪队列,反过来,B线程取鸡蛋,如果A线程要放鸡蛋,由于B线程没有释放锁,A线程处于等待状态,进入阻塞队列,取鸡蛋之后,要通知A线程放鸡蛋,A线程进入就绪队列。我们希望当盘子里有鸡蛋时,A线程阻塞,B线程就绪,盘子里没鸡蛋时,A线程就绪,B线程阻塞,代码如下:

1 importjava.util.ArrayList;2 importjava.util.List;3 /**定义一个盘子类,可以放鸡蛋和取鸡蛋*/

4 public classPlate {5 /**装鸡蛋的盘子*/

6 List eggs = new ArrayList();7 /**取鸡蛋*/

8 public synchronizedObject getEgg() {9 while (eggs.size() == 0) {10 try{11 wait();12 } catch(InterruptedException e) {13 e.printStackTrace();14 }15 }16 Object egg = eggs.get(0);17 eggs.clear();//清空盘子

18 notify();//唤醒阻塞队列的某线程到就绪队列

19 System.out.println("拿到鸡蛋");20 returnegg;21 }22 /**放鸡蛋*/

23 public synchronized voidputEgg(Object egg) {24 while (eggs.size() > 0) {25 try{26 wait();27 } catch(InterruptedException e) {28 e.printStackTrace();29 }30 }31 eggs.add(egg);//往盘子里放鸡蛋

32 notify();//唤醒阻塞队列的某线程到就绪队列

33 System.out.println("放入鸡蛋");34 }35 static class AddThread implementsRunnable  {36 privatePlate plate;37 private Object egg = newObject();38 publicAddThread(Plate plate) {39 this.plate =plate;40 }41 public voidrun() {42 plate.putEgg(egg);43 }44 }45 static class GetThread implementsRunnable  {46 privatePlate plate;47 publicGetThread(Plate plate) {48 this.plate =plate;49 }50 public voidrun() {51 plate.getEgg();52 }53 }54 public static voidmain(String args[]) {55 Plate plate = newPlate();56 for(int i = 0; i < 10; i++) {57 new Thread(newAddThread(plate)).start();58 new Thread(newGetThread(plate)).start();59 }60 }61 }

输出结果:

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

程序开始,A线程判断盘子是否为空,放入一个鸡蛋,并且唤醒在阻塞队列的一个线程,阻塞队列为空;假设CPU又调度了一个A线程,盘子非空,执行等待,这个A线程进入阻塞队列;然后一个B线程执行,盘子非空,取走鸡蛋,并唤醒阻塞队列的A线程,A线程进入就绪队列,此时就绪队列就一个A线程,马上执行,放入鸡蛋;如果再来A线程重复第一步,在来B线程重复第二步,整个过程就是生产者(A线程)生产鸡蛋,消费者(B线程)消费鸡蛋。

前段时间看了张孝祥老师线程的视频,讲述了一个其学员的面试题,也是线程通信的,在此也分享一下。

题目:子线程循环10次,主线程循环100次,如此循环100次,好像是空中网的笔试题。

1 classBoundedBuffer {2    final Lock lock = new ReentrantLock();//锁对象

3    final Condition notFull  = lock.newCondition();//写线程条件

4    final Condition notEmpty = lock.newCondition();//读线程条件

5

6    final Object[] items = new Object[100];//缓存队列

7    int putptr/*写索引*/, takeptr/*读索引*/, count/*队列中存在的数据个数*/;8

9    public void put(Object x) throwsInterruptedException {10 lock.lock();11      try{12        while (count == items.length)//如果队列满了

13          notFull.await();//阻塞写线程

14        items[putptr] = x;//赋值

15        if (++putptr == items.length) putptr = 0;//如果写索引写到队列的最后一个位置了,那么置为0

16        ++count;//个数++

17        notEmpty.signal();//唤醒读线程

18      } finally{19 lock.unlock();20 }21 }22

23    public Object take() throwsInterruptedException {24 lock.lock();25      try{26        while (count == 0)//如果队列为空

27          notEmpty.await();//阻塞读线程

28        Object x = items[takeptr];//取值

29        if (++takeptr == items.length) takeptr = 0;//如果读索引读到队列的最后一个位置了,那么置为0

30        --count;//个数--

31        notFull.signal();//唤醒写线程

32        returnx;33      } finally{34 lock.unlock();35 }36 }37  }

大家注意到没有,在调用wait方法时,都是用while判断条件的,而不是if,在wait方法说明中,也推荐使用while,因为在某些特定的情况下,线程有可能被假唤醒,使用while会循环检测更稳妥。wait和notify方法必须工作于synchronized内部,且这两个方法只能由锁对象来调用。

java线程协作_java线程系列之三(线程协作)相关推荐

  1. java set复制_Java 集合系列之三:Set基本操作

    1. Java Set 1. Java Set 重要观点 Java Set接口是Java Collections Framework的成员. Set不允许出现重复元素-----------无重复 Se ...

  2. java线程 教程_Java多线程系列教程

    Java多线程系列教程 多线程是Java中不可避免的一个重要主体.从本章开始,我们将展开对多线程的学习.接下来的内容是对Java多线程内容的讲解,涉及到的内容包括,Object类中的wait(), n ...

  3. java queue 线程安全_java并发编程之线程安全方法

    线程安全的实现方法,包含如下方式 一, 互斥同步 使用互斥锁的方式. 举个栗子 synchronized,最常用的同步实现方案, ReentrantLock,java并发包中工具,后续介绍. 互斥同步 ...

  4. java 手动线程调度_Java Thread 多线程 操作线程

    5.线程的创建和启动 A.继承Thread类或实现Runnable接口,重写或实现run方法,run方法代表线程要完成的任务 B.创建Thread子类或是Runnable的实现类,即创建的线程对象:不 ...

  5. java创建线程代码_Java创建与结束线程代码示例

    本文讲述了在Java中如何创建和结束线程的最基本方法,只针对于Java初学者.一些高级知识如线程同步.调度.线程池等内容将会在后续章节中逐步深入. 创建线程 创建普通线程有两种方式,继承Thread类 ...

  6. java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...

    线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...

  7. java线程状态_java并发编程之线程状态

    java线程中,线程状态是如何转换的呢?这一次我们一起来学习下. 线程状态: NEW: 线程创建之后,还没有启动.这时候它的状态就是NEW RUNNABLE: 正在Java虚拟机下跑任务的线程的状态. ...

  8. Java高并发和多线程系列 - 1. 线程基本概念

    1. 什么是线程? 线程和进程的区别 在了解线程的概念前,我们应该先知道什么是进程? 进程是操作系统的基本概念之一, 它是正在执行的程序实例. * 下面的一些进程的基本概念你可以了解下 ------- ...

  9. java 多线程池_Java项目中,线程池中线程数量太大会有什么影响?

    简单说一下吧!拿我们生活中非常常见的一例子来说:并不是人多就能把事情做好,增加了沟通交流成本.你本来一件事情只需要3个人做,你硬是拉来了6个人,会提升做事效率嘛?我想并不会. 线程数量过多的影响也是和 ...

  10. java 单线程执行器_Java基础-并发编程-线程执行器executor

    线程实现方式 Thread.Runnable.Callable //实现Runnable接口的类将被Thread执行,表示一个基本任务 public interface Runnable { //ru ...

最新文章

  1. 【廖雪峰python入门笔记】if语句
  2. 基于相机和激光传感器的车顶视觉检测系统
  3. 计算机学院会会,学生分会——计算机学院学生会
  4. listview 重复动画效果
  5. postgresql----JSON类型和函数
  6. 什么!在CSS中的重要意义? [重复]
  7. Postman接口调试神器-Chrome浏览器插件
  8. 小程序 获取当前用户城市信息(省市区)
  9. matlab中有哪些输出函数,MATLAB中查找并输出的函数有什么
  10. 行云管家堡垒机的使用方法之二——新增登录凭证
  11. react-native 自定义 下拉刷新 / 上拉加载更多 组件
  12. 04.Unity ShaderGraph序列(Lightweight Pipeline相关扫盲)
  13. 金字塔原理——表达的逻辑
  14. 下三角99乘法表 C语言
  15. 证明厄米矩阵不同特征值对应特征向量正交
  16. 常用的推挽输出、开漏输出、上拉输入
  17. 转载 PVE 防火墙
  18. 【RFC6582 TCP快速恢复算法的NewReno修改】(翻译)
  19. ABAP:增强篇-MIGO过账增强之CHECK方法获取行项目
  20. P4735 贪心 + 可持久化 Trie

热门文章

  1. Halcon 缺陷检测概述
  2. 判断页面环境是否在小程序的webview中
  3. 自动化运维之kickstart自动化部署安装操作系统
  4. Linux命令解释之rpm
  5. Android 7.1 App Shortcuts使用
  6. 【数据结构笔记45】通过中间步骤区分简单插入排序和归并排序
  7. Conda 环境常用碎笔记
  8. JS的构造及其事件注意点总结
  9. cacti配置流量汇总
  10. 玩游戏用什么轴的机械键盘好_机械键盘选什么轴?全方面分析各轴手感和区别...