Java学习day26:和线程相关的Object类的方法、等待线程和唤醒线程(知识点详解)

本文主要是介绍Java学习day26:和线程相关的Object类的方法、等待线程和唤醒线程(知识点详解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

声明:该专栏本人重新过一遍java知识点时候的笔记汇总,主要是每天的知识点+题解,算是让自己巩固复习,也希望能给初学的朋友们一点帮助,大佬们不喜勿喷(抱拳了老铁!)


往期回顾

Java学习day25:守护线程、死锁、线程生命周期(知识点详解)-CSDN博客

Java学习day24:线程的同步和锁(例题+知识点详解)-CSDN博客

Java学习day23:线程构造方法、常用方法(例题+知识点详解)-CSDN博客

 Java学习day26:和线程相关的Object类下面的方法、等待线程和唤醒线程

一、和线程相关的Object类的方法

1.三个常用方法

void wait()  导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
void notify()  唤醒正在等待对象监视器的单个线程。
void notifyAll() 唤醒正在等待对象监视器的所有线程。

wait()  方法换句话说,这个方法的行为就好像简单地执行呼叫`wait(0)`

二、等待线程和唤醒线程

1.什么是等待线程和唤醒线程

上面我们看了,Object类的三个主要方法,这三个主要方法就涉及到等待线程和唤醒线程,也就是说,至少两个线程,其中一个线程中使用对象.wait()  那么这个线程就会阻塞,代码不会往下执行了。也就是我们说的,等待线程。如何想让这个线程往下执行呢?再开另外一个线程,使用对象.notify()去唤醒另外那个等待线程。也就是我们说的唤醒线程,如果多个等待线程,一个唤醒线程,则调用notifyAll()方法,就能一次性唤醒所有等待线程。

我们用代码来理解

示例:

//创建这个类的目的,就是实例化出来对象,然后拿这个对象
//调用wait方法和notify方法
//wait方法和notify方法是object对象的方法
class Message {private String message;public Message(String message) {this.message = message;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}@Overridepublic String toString() {return "Message{" +"message='" + message + '\'' +'}';}}
//导致当前线程等待,直到另一个线程调用该对象的[`notify()`
class WaiterThread implements Runnable {//想一个问题?WaiterThread  使用message对象调用//wait() 咋解决?private Message msg;public WaiterThread(Message msg) {this.msg = msg;}@Overridepublic void run() {//先获取当前线程名字String name = Thread.currentThread().getName();System.out.println(name + "等待唤醒时间:" +System.currentTimeMillis());//让等待线程去阻塞,去等待  这个线程执行不下去了//锁的是msg对象synchronized (msg) {//为啥用这个wait的时候要加锁?等会讲try {msg.wait();//代码走到这,当前这个线程阻塞,不往下走了//咱们得想办法让这个等待线程继续执行下去,咋办?//在另外一个线程中去调用notify方法那么等待线程就会执行下去} catch (InterruptedException e) {e.printStackTrace();}System.out.println("123");System.out.println(name + "被唤醒的时间:" + System.currentTimeMillis());}}
}
//唤醒线程
class NotifyThread implements Runnable {//也要用同一个对象是WaiterThread线程中同一个对象调用notify()方法private Message msg;public NotifyThread(Message msg) {this.msg = msg;}@Overridepublic void run() {try {//我的想法是不能先让唤醒线程执行Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}String name = Thread.currentThread().getName();System.out.println(name + "开始唤醒等待线程");synchronized (msg) {msg.setMessage("我是修改之后的message值");msg.notify();//msg.notifyAll();//唤醒所有线程}}
}
public class Demo1 {public static void main(String[] args) {Message message = new Message("我是message属性");WaiterThread waiterThread = new WaiterThread(message);NotifyThread notifyThread = new NotifyThread(message);//如果等待线程好几个 咋办呢?new Thread(waiterThread, "等待线程1").start();// new Thread(waiterThread, "等待线程2").start();//new Thread(waiterThread, "等待线程3").start();new Thread(notifyThread, "唤醒线程").start();   }
}

代码执行结果:

        //等待线程等待唤醒时间:1660187660718   等待线程
        //唤醒线程开始唤醒等待线程        唤醒线程
        //123  等待线程
        //等待线程被唤醒的时间:1660187661740  等待线程
        //这叫线程之间的通信问题!!! 

理解这段代码核心:
先是写一个message方法用于信息显示,这个没啥好说的,然后写了两个类用来创建两个线程,分别是等待线程和唤醒线程,等待线程执行wait方法后就相当于不再执行接下来的代码,进入就绪等待状态,让唤醒线程获得cpu使用权,唤醒线程调用notify()方法后,等待线程再继续执行。同时代码里已经写清楚了,如果有多个等待线程,则唤醒线程的对象需要调用notifyAll()方法,一次性唤醒所有等待线程,而且我们必须明确的是,哪个等待线程先被唤醒是不知道的,因为抢占式运行,哪个线程先抢到cpu执行权,唤醒线程就先唤醒哪个线程。

总结:

新建两个线程:
 一个是等待线程
  线程里面的代码从上往下执行的,但是使用object.wait(),就这个方法一用,你的线程就
  阻塞了,就处于等待状态。意味着当前的代码到了wait方法以后的代码暂时不执行了
 另外一个是唤醒线程。
  唤醒线程中使用object.notify()方法,这个方法是专门唤醒刚才那个等待线程。让等待线程继续执行

同时,有一个很重要的点:调用wait()的等待线程必须加锁,具体为什么? 

wait需要持有锁的原因是,你肯定需要知道在哪个对象上进行等待,如果不持有锁,将无法做到对象变更时进行实时感知通知的作用。与此同时,为了让其他线程可以操作该值的变化,它必须要先释放掉锁,然后在该节点上进行等待。不持有锁而进行wait,可能会导致长眠不起。而且,如果不持有锁,则当wait之后的操作,都可能是错的,因为可能这个数据已经过时,其实也叫线程不安全了。总之,一切为了安全,单独的wait做不成这事。

通俗来说就是,因为我要知道对哪个对象进行唤醒的!!!
举个生活中的例子:
 大学的时候在楼底下等过自己的女朋友。
 你就是等待线程
 你女朋友就是唤醒线程
 你在楼底下等的时候  就是在wait。
 你女朋友下楼之后,唤醒你  咱们走吧。
 但是你女朋友 去唤醒别人的男朋友?这就扯犊子了,所有的加锁是为了保证是同一个对象的 

所以说,我们必须要对等待线程和唤醒线程都加锁。 


以上,就是今天的所有知识点了。等待线程和唤醒线程的代码大家要多花点时间理解,方法其实就三个,但是大家要知道怎么用的,有些代码细节都没讲到,大家要自己多花点时间,静下心看代码,写代码,多理解,多运用,重点是多去运用。

加油吧,预祝大家变得更强!

这篇关于Java学习day26:和线程相关的Object类的方法、等待线程和唤醒线程(知识点详解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/674407

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关