【面试题】notify() 和 notifyAll()方法的使用和区别

news2024/11/17 21:27:14

【面试题】notify() 和 notifyAll()方法的使用和区别

Java中notify和notifyAll的区别

何时在Java中使用notify和notifyAll?

【问】为什么wait()一定要放在循环中?

Java中通知和notifyAll方法的示例

Java中通知和notify方法的示例 


Java中notify和notifyAll的区别

Java提供了两个方法notifynotifyAll来唤醒在某些条件下等待的线程,但是Java中的notify和notifyAll之间存在细微差别

当我们使用 notify 时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器。关于线程调度的算法如下:

当我们使用 notifyAll 时,那么会唤醒所有线程,这些被唤醒的线程会去争抢锁,谁先抢到谁就先执行,其它的就要继续等待!如何实现?在循环中调用 wait 即可!

因此,notify和notifyAll之间的关键区别在于 notify只会唤醒一个线程,而notifyAll方法将唤醒所有线程

何时在Java中使用notify和notifyAll?

如果所有线程都在等待相同的条件,并且一次只有一个线程可以从条件变为true,则使用notify 和 notifyAll都可以

在上述的情况下,notify 性能优于 notifyAll,因为 notify 只需要唤醒一个线程,而 notifyAll 会唤醒所有线程,但是最后只会有一个线程继续工作,其它会继续等待,所有可以说被唤醒的大多数线程“无用”的,这种操作会浪费CPU

虽然这看起来很合理,但仍有一个警告,即无意中的接收者吞下了关键通知。通过使用notifyAll,我们确保所有收件人都会收到通知。

【问】为什么wait()一定要放在循环中?

永远在 while循环里,而不是 if语句下使用 wait()。这样,循环会在线程睡眠前后都检查 wait的条件,并在条件并未改变的情况下,处理唤醒通知。

正确写法: 

synchronized (monitor) {
    //  判断条件谓词是否得到满足
    while(!locked) {
        //  等待唤醒
        monitor.wait();
    }
    //  处理其他的业务逻辑
}

用 if 判断的话,唤醒后线程会从 wait 之后的代码开始运行,但是不会重新判断 if条件,直接继续运行 if 代码块之后的代码;

而如果使用 while 的话,也会从 wait 之后的代码运行,但是唤醒后会重新判断循环条件(重点),如果不成立再执行 while 代码块之后的代码块,成立的话继续 wait

Java中通知和notifyAll方法的示例

代码描述 

public class NotificationTest {

    private volatile boolean go = false;

    public static void main(String args[]) throws InterruptedException {
        NotificationTest test = new NotificationTest();

        Runnable waitTask = new Runnable(){
            @Override
            public void run(){
                try {
                    test.shouldGo();
                } catch (InterruptedException ex) {
                    Logger.getLogger(NotificationTest.class.getName()).
                            log(Level.SEVERE, null, ex);
                }
                System.out.println(Thread.currentThread() + " finished Execution");
            }
        };

        Runnable notifyTask = new Runnable(){
            @Override
            public void run(){
                test.go();
                System.out.println(Thread.currentThread() + " finished Execution");
            }
        };

        Thread t1 = new Thread(waitTask, "WT1");
        Thread t2 = new Thread(waitTask, "WT2");
        Thread t3 = new Thread(waitTask, "WT3");

        Thread t4 = new Thread(notifyTask,"NT1");

        t1.start();
        t2.start();
        t3.start();

        Thread.sleep(200);

        t4.start();
    }

    // waitTask 等待
    private synchronized void shouldGo() throws InterruptedException {
        while(go != true){
            System.out.println(Thread.currentThread()
                    + " is going to wait on this object");
            wait();
            System.out.println(Thread.currentThread() + " is woken up");
        }
        go = false;
    }

    // notifyTask 唤醒
    private synchronized void go() {
        while (go == false){
            System.out.println(Thread.currentThread()
                    + " is going to notify all or one thread waiting on this object");
            go = true;
            notifyAll();
        }
    }
}

运行结果 

分析

最初三个线程WT1,WT2,WT3等待,因为变量go为假,而一个线程NT1将变为真,并通过调用 notifyAll方法通知所有线程,所有线程都将被唤醒

先获得锁的那个线程将会执行结束,并修改go的值为false,这样子另外两个线程(1 和 3)将会继续等待

Java中通知和notify方法的示例 

代码描述 

public class NotificationTest {

    private volatile boolean go = false;

    public static void main(String args[]) throws InterruptedException {
        NotificationTest test = new NotificationTest();

        Runnable waitTask = new Runnable(){
            @Override
            public void run(){
                try {
                    test.shouldGo();
                } catch (InterruptedException ex) {
                    Logger.getLogger(NotificationTest.class.getName()).
                            log(Level.SEVERE, null, ex);
                }
                System.out.println(Thread.currentThread() + " finished Execution");
            }
        };

        Runnable notifyTask = new Runnable(){
            @Override
            public void run(){
                test.go();
                System.out.println(Thread.currentThread() + " finished Execution");
            }
        };

        Thread t1 = new Thread(waitTask, "WT1");
        Thread t2 = new Thread(waitTask, "WT2");
        Thread t3 = new Thread(waitTask, "WT3");

        Thread t4 = new Thread(notifyTask,"NT1");

        t1.start();
        t2.start();
        t3.start();

        Thread.sleep(200);

        t4.start();
    }

    private synchronized void shouldGo() throws InterruptedException {
        while(go != true){
            System.out.println(Thread.currentThread()
                    + " is going to wait on this object");
            wait();
            System.out.println(Thread.currentThread() + " is woken up");
        }
        go = false;
    }

    private synchronized void go() {
        while (go == false){
            System.out.println(Thread.currentThread()
                    + " is going to notify or one thread waiting on this object");
            go = true;
            notify();
        }
    }
}

运行结果 

分析

其实和上面分析的差不多,只不过由于使用的是notify,所以只会唤醒一个线程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160262.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

22年我在CSDN做到了名利兼收

写在前面 hi朋友,我是几何心凉,感谢你能够点开这篇文章,看到这里我觉得我们是有缘分的,因着这份缘分,我希望你能够看完我的分享,因为下面的分享就是要汇报给你听的,这篇文章是在 2022 年 12 月 …

从0到1完成一个Vue后台管理项目(二十三、初代项目完成、已开源)

开源地址 项目地址 项目还在优化,会增加很多新功能,UI也会重新设计,已经在修改啦! 最近打算加一些组件、顺便分享一些好用的开源项目 现在正在做迁移到vue3TS的版本、预计年后会完事,然后迁移到vite、遇到的问题和报…

docker安装prometheus和grafana

docker安装prometheus和grafana docker安装prometheus和grafana 概念简述安装prometheus 第一步:确保安装有docker第二步:拉取镜像第三步:准备相关挂载目录及文件第四步:启动容器第五步:访问测试 安装grafana 第一步&…

分享66个ASP源码,总有一款适合您

ASP源码 分享66个ASP源码,总有一款适合您 66个ASP源码下载链接:https://pan.baidu.com/s/1Jf78pfAPaFo6QhHWWHEq0A?pwdwvtg 提取码:wvtg 下面是文件的名字,我放了一些图片,文章里不是所有的图主要是放不下...&…

Docker容器与镜像命令

文章目录帮助命令镜像命令容器命令其它命令命令总结帮助命令 显示 Docker 版本信息 docker version显示 Docker 系统信息,包括镜像和容器数 docker info 帮助 docker --help 镜像命令 列出本地主机上的镜像 docker images运行结果 REPOSITORY TAG …

Python采集彼岸4K高清壁纸

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 环境使用: Python 3.8 解释器 Pycharm 编辑器 模块 import re import requests >>> pip install requests ( 更多资料、教程、文档点击此处跳转跳转文末名片加入君羊,找…

【Leetcode面试常见题目题解】5. 最长公共前缀

题目描述 本文是LC第14题&#xff0c;最长公共前缀&#xff0c;题目描述如下&#xff1a; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 限制 1 < strs.length < 200 0 < strs[i].length < 200 strs[i] 仅…

数据库 MySQL-window安装和卸载

安装 官网&#xff1a; MySQL :: Download MySQL Community Server 或 MySQL :: Download MySQL Community Server (Archived Versions) 文件目录简述 bin存放了可执行文件&#xff0c;docs是文档&#xff0c;include放的是c语言相关的.h文件&#xff0c;lib是c语言的库文件…

wmv是什么格式?如何录制wmv格式的视频?图文教学

很多小伙伴在使用文件的时候&#xff0c;经常会发现自己的一些文件后缀名是wmv。或者说在工作、学习的过程中&#xff0c;有过被要求使用wmv格式的文件。wmv是什么格式&#xff1f;如何录制wmv格式的视频&#xff1f;今天小编就来详细的跟大家说说。 一、wmv是什么格式&#xf…

SpringBoot复习(一)

底层注解 Configuration 自定义配置类 Bean: 可以通过Bean注解将方法的返回值交给ioc容器来管理 组件id为方法名&#xff0c;组件的类型就是方法的返回类型。 默认组件是单例的 Configuration: 告诉springboot这是一个配置类之前的配置文件 配置类本身也是组件&#xff0c;由s…

【Linux】Makefile/make - 快速理解入门

目录 一、概念理解 1、基本概念 2、举例说明 二、编写 Makefile 1、依赖关系和依赖方法 2、文件清理 3、扩展内容 一、概念理解 1、基本概念 在我们学习 Linux 的过程中&#xff0c;我们可以直接使用 gcc 指令对程序的文本文件逐个进行编译处理&#xff0c;这是因为我…

ASP.NET Core 3.1系列(26)——Autofac中的实例生命周期

1、前言 前面的博客主要介绍了Autofac中的一些注册方法&#xff0c;下面就来介绍一下Autofac中实例的生命周期。之前在介绍ASP.NET Core内置IoC容器的时候说过&#xff0c;实例的生命周期有&#xff1a;瞬时生命周期、域生命周期、全局单例生命周期&#xff0c;而Autofac在这三…

mysql-8.0.31-winx64详细安装教程

一、下载MySQL MySQL官网&#xff1a;https://www.mysql.com/cn/ mysql-8.0.31-winx64下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 2、下载结束后&#xff0c;解压到指定目录&#xff0c;笔者存放在D盘 &#xff0c;为求简单&#xff0c;设置目录如下&#…

数据库历史数据年度备份

数据库历史数据年度备份 1、文件说明 matomo_backup.sql 备份库表结构脚本(这个根据自己数据结构准备&#xff0c;对于时间命名的表结构就不要加了&#xff0c;只加非时间命名的表结构) export.sh 数据导出脚本 clean.sh 源数据库历史数据清除脚本 2、需求与思路 需求 对…

怎么把PDF转换成图片?来看看这几个方法吧!

要说我们手机里最多的一种文件格式是什么&#xff1f;那应该就是图片了。相信在智能手机的时代&#xff0c;每个人手机里都会有至少几百上千张照片吧。毕竟有许多的事情我们都希望通过图片、照片的形式来记录下来。所以说如何将其他格式的文件变成图片格式就成了一个不大不小的…

开发那点事(十八)Vue开发PC桌面应用案例

写在前面的话 最近有在研究electron框架&#xff0c;踩了不少坑 &#xff0c;现在把这几天研究的成果分享给大家。 研究成果 vue项目打包成exe可安装程序pc应用版本升级&#xff08;需要配合oss服务器&#xff09; vue应用配置 路由文件base配置为空mode模式为默认的hashv…

智慧门户、信创门户、国产门户、数字化门户,如何构建出七大特色亮点?

作者&#xff1a;郑文平 概述 调研结果显示&#xff0c;世界500强企业100%建设了适合自己的集团门户管理系统&#xff0c;也叫作办公门户或内网门户&#xff0c;并通过统一门户最终提升各自整体的业务管理水平和流转效率&#xff0c;没有建设门户的公司面临如下制约&#xff…

二,Spring IOC以及整合mybatis

0 复习 工厂设计模式 工厂设计模式代替new方式创建对象&#xff0c;目的是解耦合。 Spring做为工厂的使用 applicationContext.xml配置bean标签 如何从工厂中获取对象 //创建工厂 ApplicationContext ctx new ClassPathXmlApplicationContext("classpath:applicationCont…

AWS实战:Aurora到Redshift数据同步

什么是AuroraAmazon Aurora是一种基于云且完全托管关系型数据库服务&#xff0c;与MySQL 和 PostgreSQL 数据库兼容&#xff0c;完全托管意味着自动对数据库进行管理&#xff0c;包括管理数据备份、硬件配置和软件更新等操作Amazon Aurora提供了企业级性能Amazon Aurora提供了多…

【C/C++】动态顺序表详解(附完整源码)

本章内容 写在前面 1.静态与动态是指什么&#xff1f; 2.动态顺序表结构的定义 3.动态顺序表的函数接口实现 4.动态顺序表的问题及思考 5.关于顺序表的OJ题 6.OJ答案及解析 1.移除元素 2.删除有序数组中的重复项 ​3.合并两个有序数组 7.动态顺序表完整源码 1.SeqL…