我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java使用wait和notify实现线程之间的通信

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java使用wait和notify实现线程之间的通信

?一. 为什么需要线程通信

线程是并发并行的执行,表现出来是线程随机执行,但是我们在实际应用中对线程的执行顺序是有要求的,这就需要用到线程通信

?线程通信为什么不使用优先级来来解决线程的运行顺序?

总的优先级是由线程pcb中的优先级信息和线程等待时间共同决定的,所以一般开发中不会依赖优先级来表示线程的执行顺序

?看下面这样的一个场景:面包房的例子来描述生产者消费者模型

有一个面包房,里面有面包师傅和顾客,对应我们的生产者和消费者,而面包房有一个库存用来存储面包,当库存满了之后就不在生产,同时消费者也在购买面包,当库存面包卖完了之后,消费者必须等待新的面包生产出来才能继续购买

分析:对于何时停止生产何时停止消费就需要应用到线程通信来准确的传达生产和消费信息

?二. wait和notify方法

?wait():让当前线程持有的对象锁释放并等待

?wait(long timeout):对应的参数是线程等待的时间

?notify():唤醒使用同一个对象调用wait进入等待的线程,重新竞争对象锁

?notifyAll():如果有多个线程等待,notifyAll是全部唤醒 ,notify是随机唤醒一个

?‍?️注意:

?这几个方法都属于Object类中的方法

?必须使用在synchronized同步代码块/同步方法中

?哪个对象加锁,就是用哪个对象wait,notify

?调用notify后不是立即唤醒,而是等synchronized结束以后,才唤醒

?1. wait()方法

?调用wait方法后: 

?使执行当前代码的线程进行等待(线程放在等待队列)

?释放当前的锁

?满足一定条件时被唤醒,重新尝试获取锁

?wait等待结束的条件:

?其他线程调用该对象的notify方法

?wait等待时间超时(timeout参数来指定等待时间)

?其他线程调用interrupted方法,导致wait抛出InterruptedException异常

?2. notify()方法 

当使用wait不带参数的方法时,唤醒线程等待就需要使用notify方法

?这个方法是唤醒那些等待该对象的对象锁的线程,使他们可以重新获取该对象的对象锁 

?如果有多个线程等待,则由线程调度器随机挑选出一个呈wait 状态的线程(不存在先来后到)

?在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁

?3. notifyAll()方法

该方法和notify()方法作用一样,只是唤醒的时候,将所有等待的线程都唤醒

notify()方法只是随机唤醒一个线程 

?三. 使用wait和notify实现面包房业务 

前提说明:

有2个面包师傅,面包师傅一次可以做出两个面包

仓库可以存储100个面包

有10个消费者,每个消费者一次购买一个面包 

?‍?️注意:

消费和生产是同时并发并行进行的,不是一次生产一次消费

?‍?️实现代码:

public class Bakery {
    private static int total;//库存
 
    public static void main(String[] args) {
        Producer producer = new Producer();
        for(int i = 0;i < 2;i++){
            new Thread(producer,"面包师傅-"+(i-1)).start();
        }
        Consumer consumer = new Consumer();
        for(int i = 0;i < 10;i++){
            new Thread(consumer,"消费者-"+(i-1)).start();
        }
    }
    private static class Producer implements Runnable{
        private int num = 3; //生产者每次生产三个面包
        @Override
        public void run() {
            try {
                while(true){ //一直生产
                    synchronized (Bakery.class){
                        while((total+num)>100){ //仓库满了,生产者等待
                            Bakery.class.wait();
                        }
                        //等待解除
                        total += num;
                        System.out.println(Thread.currentThread().getName()+"生产面包,库存:"+total);
                        Thread.sleep(500);
                        Bakery.class.notifyAll(); //唤醒生产
                    }
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private static class Consumer implements Runnable{
        private int num = 1; //消费者每次消费1个面包
        @Override
        public void run() {
            try {
                while(true){ //一直消费
                    synchronized (Bakery.class){
                        while((total-num)<0){ //仓库空了,消费者等待
                            Bakery.class.wait();
                        }
                        //解除消费者等待
                        total -= num;
                        System.out.println(Thread.currentThread().getName()+"消费面包,库存:"+total);
                        Thread.sleep(500);
                        Bakery.class.notifyAll(); //唤醒消费
                    }
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

部分打印结果:

?四. 阻塞队列

阻塞队列是一个特殊的队列,也遵循“先进先出”的原则,它是线程安全的队列结构

特性:典型的生产者消费者模型,一般用于做任务的解耦和消峰

?队列满的时候,入队列就堵塞等待(生产),直到有其他线程从队列中取走元素
?队列空的时候,出队列就堵塞等待(消费),直到有其他线程往队列中插入元素 

?1. 生产者消费者模型 

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题

生产者和消费者彼此之间不直接通信,而通过阻塞队列来进行通信,所以生产者生产完数据之后等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取

阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力
阻塞队列也能使生产者和消费者之间解耦

上述面包房业务的实现就是生产者消费者模型的一个实例

?2. 标准库中的阻塞队列

在 Java 标准库中内置了阻塞队列, 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可

?BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue

?put 方法用于阻塞式的入队列, take 用于阻塞式的出队列

?BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性

        BlockingDeque<String> queue = new LinkedBlockingDeque<>();
        queue.put("hello");
        //如果队列为空,直接出出队列就会阻塞
        String ret = queue.take();
        System.out.println(ret);

?3. 阻塞队列的模拟实现

这里使用数组实现一个循环队列来模拟阻塞队列

?当队列为空的时候,就不能取元素了,就进入wait等待,当有元素存放时,唤醒

?当队列为满的时候,就不能存元素了,就进入wait等待,当铀元素取出时,唤醒 

?‍?️实现代码:

public class MyBlockingQueue {
    //使用数组实现一个循环队列,队列里面存放的是线程要执行的任务
    private Runnable[] tasks;
    //队列中任务的数量,根据数量来判断是否可以存取
    private int count;
    private int putIndex; //存放任务位置
    private int takeIndex; //取出任务位置
 
    //有参的构造方法,表示队列容量
    public MyBlockingQueue(int size){
        tasks = new Runnable[size];
    }
 
    //存任务
    public void put(Runnable task){
        try {
            synchronized (MyBlockingQueue.class){
                //如果队列容量满了,则存任务等待
                while(count == tasks.length){
                    MyBlockingQueue.class.wait();
                }
                tasks[putIndex] = task; //将任务放入数组
                putIndex = (putIndex+1) % tasks.length; //更新存任务位置
                count++; //更新存放数量
                MyBlockingQueue.class.notifyAll(); //唤醒存任务
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    //取任务
    public Runnable take(){
        try {
            synchronized (MyBlockingQueue.class){
                //如果队列任务为空,则取任务等待
                while(count==0){
                    MyBlockingQueue.class.wait();
                }
                //取任务
                Runnable task = tasks[takeIndex];
                takeIndex = (takeIndex+1) % tasks.length; //更新取任务位置
                count--; //更新存放数量
                MyBlockingQueue.class.notifyAll(); //唤醒取任务
                return task;
            }
        } catch (InterruptedException e) {
           throw new RuntimeException("存放任务出错",e);
        }
    }
}

?五. wait和sleep的区别(面试题)

相同点:

都可以让线程放弃执行一段时间 

不同点:

☘️wait用于线程通信,让线程在等待队列中等待

☘️sleep让线程阻塞一段时间,阻塞在阻塞队列中

☘️wait需要搭配synchronized使用,sleep不用搭配

☘️wait是Object类的方法,sleep是Thread的静态方法

总结

到此这篇关于Java使用wait和notify实现线程之间的通信的文章就介绍到这了,更多相关Java wait和notify实现线程通信内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java使用wait和notify实现线程之间的通信

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java如何使用wait/notify实现线程间通信

本文小编为大家详细介绍“Java如何使用wait/notify实现线程间通信”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java如何使用wait/notify实现线程间通信”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习
2023-07-04

浅谈Java线程间通信之wait/notify

Java中的wait/notify/notifyAll可用来实现线程间通信,是Object类的方法,这三个方法都是native方法,是平台相关的,常用来实现生产者/消费者模式。先来我们来看下相关定义:wait() :调用该方法的线程进入WA
2023-05-31

Java中怎么使用wait和notify实现线程间的通信

这篇“Java中怎么使用wait和notify实现线程间的通信”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中怎么使
2023-06-30

Java怎么使用wait或notify实现线程间通信

这篇文章主要介绍“Java怎么使用wait或notify实现线程间通信”,在日常操作中,相信很多人在Java怎么使用wait或notify实现线程间通信问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java怎
2023-07-04

Java通过wait()和notifyAll()方法实现线程间通信

本文实例为大家分享了Java实现线程间通信的具体代码,供大家参考,具体内容如下Java代码(使用了2个内部类):package Threads;import java.util.LinkedList;/** * Created by Fra
2023-05-31

Java中线程之间的通信是如何实现的

这篇文章将为大家详细讲解有关Java中线程之间的通信是如何实现的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。正常情况下,每个子线程完成各自的任务就可以结束了。不过有的时候,我们希望多个线程
2023-05-31

Java编程之多线程死锁与线程间通信简单实现代码

死锁定义 死锁是指两个或者多个线程被永久阻塞的一种局面,产生的前提是要有两个或两个以上的线程,并且来操作两个或者多个以上的共同资源;我的理解是用两个线程来举例,现有线程A和B同时操作两个共同资源a和b,A操作a的时候上锁LockA,继续执行
2023-05-30

使用Java怎么实现线程之间的共享和协作

这篇文章将为大家详细讲解有关使用Java怎么实现线程之间的共享和协作,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、线程间的共享1.1 ynchronized内置锁用处Java支持多个线程
2023-06-14

在Java中,如何实现多线程之间的同步与通信?(Java多线程编程时,应如何确保线程间的同步与有效通信?)

本文详细介绍了Java多线程编程中的同步和通信机制。为了实现同步,可以使用synchronized关键字、Lock接口或Semaphore。同步确保多个线程在访问共享资源时保持一致性。对于通信,共享内存、消息队列和管道等机制允许线程共享数据和协调任务。文中还提供了代码示例、最佳实践和问题的预防策略。通过这些机制,开发者可以创建同步且有效的Java多线程应用程序。
在Java中,如何实现多线程之间的同步与通信?(Java多线程编程时,应如何确保线程间的同步与有效通信?)
2024-04-02

使用DNode实现php和nodejs之间通信的简单实例

一、安装DNode 1, for nodejs, 执行$ sudo npm install dnode2, for php, 利用composer来安装DNode php 执行下列语句下载composer$ wget http://getc
2022-06-04

Java中的单线程通信如何利用 Socket实现

这期内容当中小编将会给大家带来有关Java中的单线程通信如何利用 Socket实现,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java Socket实现单线程通信的方法,具体如下:现在做Java直接使用
2023-05-31

Java中利用管道实现线程间的通讯(转)

Java中利用管道实现线程间的通讯(转)[@more@]在java 语言中,提供了各种各样的输入输出流(stream),使我们能够很方便的对数据进行操作,其中,管道(pipe)流是一种特殊的流,用于在不同线程(threads)间直接传送数据
2023-06-03

Java项目中的多线程通信如何利用Socket实现

这期内容当中小编将会给大家带来有关Java项目中的多线程通信如何利用Socket实现,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java Socket实现多线程通信的方法,代码如下:package co
2023-05-31

Handler实现线程之间的通信下载文件动态更新进度条

1. 原理每一个线程对应一个消息队列MessageQueue,实现线程之间的通信,可通过Handler对象将数据装进Message中,再将消息加入消息队列,而后线程会依次处理消息队列中的消息。2. Message初始化:一般使用Messag
2023-05-30

SpringBoot线程池和Java线程池的使用和实现原理解析

这篇文章主要介绍了SpringBoot线程池和Java线程池的用法和实现原理,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-05-15

怎么使用C++ Thread实现简单的socket多线程通信

本篇内容介绍了“怎么使用C++ Thread实现简单的socket多线程通信”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!服务端多线程功能放
2023-07-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录