线程的等待通知机制

news2025/4/15 5:16:26

线程的等待通知机制

  • 一:情景再现:
  • 二:等待通知机制:
    • 2.1 wait()方法
    • 2.2 notify()方法
      • 2.22:唤醒了t2线程,t1线程仍处于阻塞等待状态
      • 2.23 唤醒了t1线程,t2线程仍处于阻塞等待状态
      • 2.24:notifyAll()

一:情景再现:

假设有3个滑稽,1号滑稽在ATM中取钱,2,3号滑稽只能在门口阻塞等待,1号滑稽发现ATM中没钱了,就从ATM中出来了,在2号滑稽进去之前,1号滑稽又进去了,2号又只能阻塞等待,1号发现仍然没钱,又出来了,然后再2号进去之前,1号 又进去了,就这样,1号进进出出,其他滑稽只能阻塞等待.
上述问题,就称为"线程饿死":线程调度是随机的,很可能出现某个线程频繁的获取释放锁,由于获取的太快,以至于其他 线程捞不到CPU资源,导致程序的效率降低.

二:等待通知机制:

等待通知机制,就可以解决上述问题.
当2号滑稽进ATM后,存了一部分钱,告诉1号滑稽,一号滑稽就可以去取钱了.
通过条件,判断当前逻辑是否能够执行,如果不能执行,就主动wait(主动进行阻塞),就把执行的机会让给别的线程了,避免该线程进行一些无意义的重试.等后续条件实际成熟了(需要其他线程通知),再让阻塞的线程被唤醒.

2.1 wait()方法

wait()方法,是Object提供的方法,任何一个对象,都有这个方法.
wait()内部做的事情,不仅仅是阻塞等待,还要解锁,解锁的同时,进行等待.解锁之后,其他线程就可以获取到锁对象了
要想解锁,那么首先就必须有锁,所以wait()方法往往是和synchronized关键字搭配使用的.

public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        Object object=new Object();
        System.out.println("解锁之前");
        synchronized (object){
            object.wait();
        }
        System.out.println("解锁之后");
    }
}

此时main线程就进入阻塞等待状态,直到其他线程将他唤醒.
通过jconsole也可以看到main线程的状态.
在这里插入图片描述

2.2 notify()方法

通过另一个线程,去唤醒阻塞的线程.

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Object object=new Object();
        Thread t1=new Thread(()->{
            synchronized (object){
                System.out.println("t1 等待之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 等待之后");
            }
        });
        Thread t2=new Thread(()->{
            Scanner scanner=new Scanner(System.in);
            synchronized (object){
                System.out.println("t2 通知之前");
                scanner.next();
                object.notify();
                System.out.println("t2 通知之后");
            }
        });

      //  Thread.sleep(1000);

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

    }
}

当用户时输入内容之后,此时就会使t2线程调度到CPU上,然后才进一步的执行到notify,notify就会唤醒wait()操作,从而使t1回到RUNNABLE状态参与调度.
当然,把t1唤醒,t1是不能立即就能执行的,t1要重新获取到锁.
由于此时t2还没有释放锁,意味着t1会从WAITING->RUNNABLE->BLOCKED
因为t1 ,t2执行抢占式执行,执行顺序不确定,那么就有可能t2先执行了notify,此时t1还没wait,那么notify就不会有任何效果(也不会抛异常),但是后续t1进入wait()之后,就没有人能够唤醒了.

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Object object=new Object();
        Thread t1=new Thread(()->{
            synchronized (object){
                System.out.println("t1 等待之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 等待之后");
            }
        });
        Thread t2=new Thread(()->{
            Scanner scanner=new Scanner(System.in);//借助 scanner 控制阻塞. 用户输入之前, 都是阻塞状态.
            synchronized (object){
                System.out.println("t2 通知之前");
                scanner.next();
                object.notify();
                System.out.println("t2 通知之后");
            }
        });

      //  Thread.sleep(1000);

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

    }
}

上述代码t1线程就一直处于阻塞状态,进程也不会结束了.

如果有多个要唤醒的线程,那么唤醒哪一个是随机的.
那么就需要多个锁对象,不同的线程使用不同的锁对象来wait,唤醒的时候,再根据锁对象去唤醒.

2.22:唤醒了t2线程,t1线程仍处于阻塞等待状态

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Object object=new Object();
        Object object2=new Object();
        Thread t1=new Thread(()->{
            synchronized (object){
                System.out.println("t1 等待之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 等待之后");
            }
        });
        Thread t2=new Thread(()->{
            synchronized (object2){
                System.out.println("t2 等待之前");
                try {
                    object2.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 等待之后");
            }
        });
        Thread t3=new Thread(()->{
            synchronized (object2){
                System.out.println("t3 通知之前");
                object2.notify();//唤醒了t2线程,t1线程仍处于阻塞等待状态
                System.out.println("t3 通知之后");
            }
        });



        t1.start();
        t2.start();
        Thread.sleep(1000);
        t3.start();

    }
}

在这里插入图片描述

2.23 唤醒了t1线程,t2线程仍处于阻塞等待状态

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Object object=new Object();
        Object object2=new Object();
        Thread t1=new Thread(()->{
            synchronized (object){
                System.out.println("t1 等待之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 等待之后");
            }
        });
        Thread t2=new Thread(()->{
            synchronized (object2){
                System.out.println("t2 等待之前");
                try {
                    object2.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 等待之后");
            }
        });
        Thread t3=new Thread(()->{
            synchronized (object){
                System.out.println("t3 通知之前");
                object.notify();//唤醒了t1线程,t2线程仍处于阻塞等待状态
                System.out.println("t3 通知之后");
            }
        });



        t1.start();
        t2.start();
        Thread.sleep(1000);
        t3.start();

    }
}

在这里插入图片描述

2.24:notifyAll()

唤醒所有等待的线程(锁对象相同).

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Object object=new Object();
        Object object2=new Object();

        Thread t1=new Thread(()->{
            synchronized (object){
                System.out.println("t1 等待之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 等待之后");
            }
        });
        Thread t2=new Thread(()->{
            synchronized (object){
                System.out.println("t2 等待之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 等待之后");
            }
        });

        Thread t4=new Thread(()->{
            synchronized (object2){
                System.out.println("t4 等待之前");
                try {
                    object2.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t4 等待之后");
            }
        });
        Thread t3=new Thread(()->{
            synchronized (object){
                System.out.println("t3 通知之前");
                object.notifyAll();//唤醒所有的因object锁引起的阻塞等待,但t4线程不会被唤醒
                System.out.println("t3 通知之后");
            }
        });



        t1.start();
        t2.start();
        t4.start();
        Thread.sleep(1000);
        t3.start();

    }
}

在这里插入图片描述

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

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

相关文章

VuePress基于 Vite 和 Vue 构建优秀框架

VitePress 是一个静态站点生成器 (SSG),专为构建快速、以内容为中心的站点而设计。简而言之,VitePress 获取用 Markdown 编写的内容,对其应用主题,并生成可以轻松部署到任何地方的静态 HTML 页面。 VitePress 附带一个用于技术文档…

Vmware下减小Ubuntu系统占用系统盘大小

1、虚拟机设置下占用空间 如图,给虚拟机分配了120GB,已经占用116.9GB,开机会提示空间不足。 2、实际使用空间 ubuntu系统下使用“df -h”命令查看实际使用空间大小50GB左右 造成这个原因是,虚拟机的bug:在虚拟机的ub…

【递归】有序分数(SBT)

给定一个整数 N,请你求出所有分母小于或等于 N,大小在 [0,1][0,1] 范围内的最简分数,并按从小到大顺序依次输出。 例如,当 N5时,所有满足条件的分数按顺序依次为: 0/1,1/5,1/4,1/3,2/5,1/2,3/5,2/3,3/4,4…

二叉树寻找祖先问题-算法通关村

二叉树寻找祖先问题-算法通关村 1 最近公共祖先问题 LeetCode236:给定一个二叉树,找到该树中两个指定节点的最近公共祖先。 最近公共祖先的定义为:“对于有根树T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足是…

Docket常见的软件部署1

1 安装MySQL # 查看MySQL镜像 docker search mysql # 拉起镜像 docker pull mysql:5.7 # 创建MySQL数据映射卷,防止数据不丢失 mkdir -p /hmoe/tem/docker/mysql/data/ # 启动镜像 docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v /home…

7_springboot_shiro_jwt_多端认证鉴权_自定义AuthenticationToken

1. 目标 ​ 本小节会先对Shiro的核心流程进行一次回顾,并进行梳理。然后会介绍如果应用是以API接口的方式提供给它方进行调用,那么在这种情况下如何使用Shiro框架来完成接口调用的认证和授权。 2. 核心架构 引用官方的架构图: 2.1 Subje…

蓝桥杯第十五届抱佛脚(八)并查集

蓝桥杯第十五届抱佛脚(八)并查集 基本概念 并查集是一种数据结构,用于管理一系列不交集的元素集合,并支持两种操作: 查找(Find): 查找操作用于确定某个元素属于哪个集合&#xf…

Topaz Photo AI for Mac v2.4.2 智能AI降噪软件

Topaz Photo AI是一款适用于Mac的图像处理软件,使用人工智能技术对照片进行编辑和优化。该软件提供了多种强大的功能,包括降噪、锐化、消除噪点、提高分辨率等,可以帮助用户改善图像质量,并实现自定义的效果。 软件下载&#xff1…

前端-html-02

1.列表 标签名功能和语义属性单标签还是双标签ul无序列表包裹元素双标签 ol 有序列表包裹元素双标签li列表项双标签dl定义列表包裹元素双标签dt定义列表项标题双标签dd定义列表项描述双标签 li必须由Ul或者ol包裹 <!DOCTYPE html> <html><head><…

Web APIs知识点讲解(阶段七)

正则表达式 1.能够利用正则表达式校验输入信息的合法性2. 具备利用正则表达式验证小兔鲜注册页面表单的能力 一.正则表达式 1.正则表达式 正则表达式&#xff08;Regular Expression&#xff09;是用于匹配字符串中字符组合的模式。在 JavaScript中&#xff0c;正则表达式也…

我们正在被 DDoS 攻击,但是我们啥也不干,随便攻击...

最近&#xff0c;一场激烈的攻防大战在网络世界悄然上演。 主角不是什么国家安全局或者黑客组织&#xff0c;而是一家名不见经传的创业公司——TablePlus。 DDoS 攻击者们摩拳擦掌&#xff0c;跃跃欲试。他们从四面八方蜂拥而至&#xff0c;誓要用数亿次请求把 TablePlus 的服…

Redis 常见数据结构及命令

目录 一.Redis常见的数据结构 二.Redis数据结构对应的命令 1.String类型 2.Hash类型 3.List类型 4.Set类型 5.Sorted Set类型 一.Redis常见的数据结构 Redis支持多种数据结构&#xff0c;包括字符串&#xff08;string&#xff09;、哈希&#xff08;hash&#xff09;、…

STM32的芯片无法在线调试的情况分析

问题描述 本博客的目的在于帮助网友尽快地解决问题&#xff0c; 避免浪费时间&#xff0c; 查漏补缺。 在stm32的开发过程中&#xff0c;有时会遇到"STM No Target connected"的错误提示&#xff0c;这说明MDK开发环境无法与目标设备进行通信&#xff0c;导致无法烧…

【JavaSE】类和对象详解(上)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 类和对象 类的组成 对类的理解 成员变量的访问和类方法的调用 this 抛出一个问题 this的作用 初始化成员变量 未初始化的成员变量 代码举例 就地初始化 构…

Autodesk Maya 2025 mac玛雅三维动画特效软件

Autodesk Maya 2025 for Mac是一款功能强大、操作简便的三维动画软件&#xff0c;适用于电影、电视、游戏、建筑、工业设计、虚拟现实和动画等领域。无论是专业设计师还是初学者&#xff0c;都可以通过Maya 2025实现自己的创意和想法&#xff0c;创作出高质量的三维作品。 软件…

浅谈Spring体系的理解

浅谈Spring知识体系 Spring Framework架构图Spring家族技术生态全景图XMind汇总 本文不涉及细节&#xff0c;主要回答两个问题&#xff1a; Spring家族技术生态全景图有哪些Spring Framework架构下每个模块有哪些东西&#xff0c;以及部分模块之间的关联关系 Spring Framework架…

探究贪心算法:特点与实际应用

探究贪心算法&#xff1a;特点与实际应用 探究贪心算法&#xff1a;特点与实际应用&#x1f4dd; 摘要&#x1f680; 引言&#x1f4cb; 正文内容&#xff08;详细介绍&#xff09;&#x1f4cc; 小结&#x1f4ca; 表格总结&#x1f3af; 总结&#x1f52e; 未来展望&#x1f…

【Redis教程0x08】详解Redis过期删除策略内存淘汰策略

引言 Redis的过期删除策略和内存淘汰策略是经常被问道的问题&#xff0c;这两个机制都是做删除操作&#xff0c;但是触发的条件和使用的策略是不同的。今天就来深入理解一下这两个策略。 过期删除策略 Redis 是可以对 key 设置过期时间的&#xff0c;因此需要有相应的机制将…

智能文档合规检测系统:在央企国企招标采购领域的应用

一、背景介绍 在央企国企采购过程中&#xff0c;合规性是一个不可忽视的重要方面。采购方需要确保供应商的资质、业绩、规模等条件符合采购要求&#xff0c;同时避免设置不合理的条件限制或排斥潜在供应商。为了提高采购效率和确保合规性&#xff0c;智能文档合规检测系统应运…

40.网络游戏逆向分析与漏洞攻防-角色管理功能通信分析-角色删除功能的数据包失败的分析

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a; 易道云信息技术研究院VIP课 上一个内容&#xff1a;39.角色数据的维…