多线程——线程通信

在现实应用中,很多时候需要让多个线程按照一定的次序来访问共享资源。例如,经典的生产者和消费者问题。

① 这类问题描述了这样一种情况,假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费。如果仓库中没有产品,则生产者可以将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,知道仓库中再次放入产品为止。

② 显然,这是一个同步问题,生产者和消费者共享同一资源,并且,生产者和消费者之间彼此依赖,互为条件向前推进。

该如何变成程序来解决这个问题呢?

传统的思路是利用循环检测的方式来实现,通过重复检查某一个特定条件是否成立来决定线程的推进顺序。

比如,一旦生产者生产结束,它就继续利用循环检测来判断仓库中的产品是否被消费者消费,而消费者也是在消费结束后就会立即使用循环检测的方式来判断仓库中是否又放进产品。显然,这些操作是很耗CPU资源的,不值得提倡。

有没有更好的方法来解决这类问题呢?

Java提供了3个重要的方法巧妙解决线程间的通信问题。这3个方法分别是:wait()、notify()和notifyAll()。

① wait():可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。

② notify():可以唤醒等待队列中第一个等待同一共享资源的线程,并使该线程退出等待队列,进入可运行状态。

③ notifyAll():可以使所有正在等待队列中同一共享资源的线程从等待队列状态退出,进入可运行状态,此时,优先级最高的那个线程最先执行。

示例:生产者采摘5个苹果放入篮子中,消费者拿走篮子中的5个苹果后,生产者再采摘5个放入篮子…共进行4次。

代码如下:

  1 //消费者线程
  2 class Consumer extends Thread {
  3     private Basket basket = null;
  4
  5     public Consumer(Basket basket) {
  6         super();
  7         this.basket = basket;
  8     }
  9
 10     @Override
 11     public void run() {
 12         basket.popApple();
 13     }
 14
 15 }
 16
 17 //生产者线程
 18 class Productor extends Thread {
 19     private Basket basket = null;
 20
 21     public Productor(Basket basket) {
 22         super();
 23         this.basket = basket;
 24     }
 25
 26     @Override
 27     public void run() {
 28         basket.pushApple();
 29     }
 30
 31 }
 32
 33 //篮子类
 34 class Basket {
 35     private LinkedList<Apple> basket = new LinkedList<Apple>();
 36
 37     // 放4轮苹果
 38     public synchronized void pushApple() {
 39         for (int i = 0; i < 20; i++) {
 40             Apple apple = new Apple(i);
 41             push(apple);
 42         }
 43     }
 44
 45     // 取4轮苹果
 46     public synchronized void popApple() {
 47         for (int i = 0; i < 20; i++) {
 48             pop();
 49         }
 50     }
 51
 52     // 向篮子中放苹果
 53     private void push(Apple apple) {
 54         // 当篮子中存放了5个苹果就等待并通知消费者来消费
 55         if (basket.size() == 5) {
 56             try {
 57                 wait();
 58             } catch (InterruptedException e) {
 59                 e.printStackTrace();
 60             }// 等待并释放当前资源的锁
 61         }
 62         try {
 63             Thread.sleep(500);
 64         } catch (InterruptedException e) {
 65             e.printStackTrace();
 66         }
 67         basket.addFirst(apple);
 68         System.out.println("存放:" + apple.toString());
 69         notify();// 通知消费者来消费
 70     }
 71
 72     // 向篮子中取苹果
 73     private void pop() {
 74         // 当篮子中苹果为0时就等待并通知生产者来生产
 75         if (basket.size() == 0) {
 76             try {
 77                 wait();
 78             } catch (InterruptedException e) {
 79                 e.printStackTrace();
 80             }// 等待并释放当前资源的锁
 81         }
 82         try {
 83             Thread.sleep(500);
 84         } catch (InterruptedException e) {
 85             e.printStackTrace();
 86         }
 87         Apple apple = basket.removeFirst();
 88         System.out.println("吃掉:" + apple.toString());
 89         notify();// 通知生产者来生产
 90     }
 91 }
 92
 93 // 苹果类
 94 class Apple {
 95     private int id;
 96
 97     public Apple(int id) {
 98         this.id = id;
 99     }
100
101     @Override
102     public String toString() {
103         return "苹果:" + (id + 1);
104     }
105 }

主方法:

1         Basket basket=new Basket();
2         Productor productor=new Productor(basket);
3         Consumer consumer=new Consumer(basket);
4         productor.start();
5         consumer.start();

运行结果:

生产者逐个生产5个苹果1-5;消费者逐个消费5个苹果5-1;
生产者逐个生产5个苹果6-10;消费者逐个消费5个苹果10-6;
生产者逐个生产5个苹果11-15;消费者逐个消费5个苹果15-11;
生产者逐个生产5个苹果16-20;消费者逐个消费5个苹果20-16;

时间: 2024-10-12 21:48:50

多线程——线程通信的相关文章

java多线程 - 线程通信

当线程在系统内运行时,程序通常无法准确控制线程的轮换执行,但是可以通过一些机制来保证线程协调运行. 由同步监视器对象协调线程 实现这种功能可以借助于Object类提供的wait().notify().notifyAll()三个方法(注意,这三个方法属于Object类,不属于Thread类).这三个方法必须由同步监视器来调用,可以分为两种情况: 对于同步方法,同步监视器默认是当前实例(this),所以可以在同步方法中直接调用这三个方法: 对于同步代码块,同步监视器是synchronized后括号里

java多线程——线程通信

一.线程通信目标 1.线程通信的目标是使线程间能够互相发送信号 2.线程通信使线程能够等待其他线程的信号 二.几种方式 1.通过共享对象 2.忙等待 线程 B 运行在一个循环里,以等待信号 (不释放cpu) 3.wait,notify和notifyAll wait会使线程进入睡眠或者非运行状态,释放cpu使用权: 线程必须在同步块里调用 wait()或者 notify(): 当一个线程调用一个对象的 notify()方法,正在等待该对象的所有线程中将有一个线程被唤醒并允许执行(校注:这个将被唤醒

C++多线程の线程通信future,promise,async

多线程-线程间通信

1.多线程-线程间通信-问题演示 多线程间通信:多个线程处理同一资源,但是处理动作却不同. //创建一个资源描述.资源中有name sex.用于存储数据. class Resource{     String name;     String sex; } //需要定义一个输入任务描述.既然是线程任务,必须实现Runnable接口.  class Input implements Runnable{     private Resource r;     Input(Resource r){  

JavaSE:多线程补充--线程通信

线程通信我认为是多线程中最难掌握的部分了,这里通过两个例子来说明一下. 第一个: 使用两个线程打印 1-100. 线程1, 线程2 交替打印 public class Print implements Runnable{ int i = 1; public void run(){ while(true){ synchronized(this){ if(i<100){ notify(); System.out.println(Thread.currentThread().getName() + &qu

多线程-线程间通信-多生产者多消费者示例

1.多线程-线程间通信-多生产者多消费者问题 多生产者和多消费者.等待唤醒机制. 产生了两个问题: 1.出现了多次连续生产,未消费,或者一个商品被消费多次. 解决:必须要--------每一个被唤醒的线程判断一次标记,所以将if判断改为while判断. 2.出现了死锁. 本方唤醒了本方,导致了所有的线程都等待了. 解决方式就是:唤醒所有等待的线程.这样既唤醒了本方也唤醒对方. 虽然解决了多生产消费的问题,但是有些低效. 解决方法一: 唤醒所有等待的线程 class Resource{     p

Qt多线程程序设计中,可使用信号和槽进行线程通信

Qt多线程程序设计中,可使用信号和槽进行线程通信.下面是一个简单的示例. 该程序实现了线程中自定义一个信号和槽,定时1秒发送信号,槽响应后打印一条信息. [cpp] view plain copy  #include <QtCore/QCoreApplication> #include <QThread> #include <stdio.h> class MyThread:public QThread { Q_OBJECT public: MyThread(); voi

JAVA多线程提高二:传统线程的互斥与同步&amp;传统线程通信机制

本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备. 一.线程的互斥 为什么会有线程的互斥?可以想银行取款的问题,如果不做监控,多个人同时针对一个存折取钱的时候就会出现钱不对的问题,下面我们通过两个例子来分析一下线程的互斥问题以及为什么会产生这个线程? 例子1:一个人生产信息,一个人消费信息 面向对象的思想:类 信息类 生产者 消费者 public class TriditionalTh

关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文)

Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享给大家. 一.对于线程同步和同步锁的理解(注:分享了三篇高质量的博客) 以下我精心的挑选了几篇博文,分别是关于对线程同步的理解和如何选择线程锁以及了解线程锁的作用范围. <一>线程同步锁的选择 1. 这里我推荐下Java代码质量改进之:同步对象的选择这篇博文. 2. 以上推荐的博文是以卖火车票为例