【多线程】线程间通信及状态

文章目录

    • 1. 线程间的通信
      • 1.1 wait和notify
      • 1.2 notify随机唤醒
      • 1.3 notifyAll()
      • 1.4 join()
    • 2. 线程间的状态
    • 3. 验证线程的状态
      • 3.1 验证NEW、RUNNABLE、TERMINATED
      • 3.2 验证WAITING
      • 3.3 验证TIMED-WAITING
      • 3.4 验证BLOCKED
    • 4. 面试题:wait和sleep对比

1. 线程间的通信

1.1 wait和notify

由于线程之间是抢占式运行,所以无法预知线程的执行顺序,但是实际开发中我们需要协调线程间的执行先后顺序。所以我们引入了等待通知机制。
wait()方法:
会使一个线程进入堵塞,进入等待状态,等待被唤醒,不然永远不会执行,但必须搭配synchronized使用,不然就会抛出异常,执行后释放锁。
notify()方法:
会随机唤醒一个等待的线程(不是先来后到),也需要搭配synchronized使用,但是只有执行完同步代码块才会释放锁。
notifyAll()方法
一次将所有等待的线程唤醒,其他和wait方法相同。

创建线程类ThreadA.java:

public class MyThreadA extends Thread{private Object lock;public MyThreadA(Object lock){this.lock  = lock;}@Overridepublic void run() {try{synchronized (lock){System.out.println(Thread.currentThread().getName() + ": begin");lock.wait();//死等,释放锁System.out.println(Thread.currentThread().getName() + ": end");}} catch (InterruptedException e){e.printStackTrace();}}
}

创建线程类ThreadB.java:

public class MyThreadB extends Thread{private Object lock;public MyThreadB(Object lock){this.lock = lock;}@Overridepublic void run() {synchronized (lock){System.out.println("t2: begin");lock.notify();//唤醒一个线程,随机的,不释放锁System.out.println("t2: end");}}
}

创建运行类Running.java:

public class Running {public static void main(String[] args) throws InterruptedException {Object lock = new Object();MyThreadA t1 = new MyThreadA(lock);MyThreadB t2 = new MyThreadB(lock);t1.start();Thread.sleep(1000);t2.start();}
}

如果等待机制成立,那么运行过程应该是先启动线程t1,然后打印"Thread-0: begin",然后执行wait(),进入等待并释放锁,然后在启动t2后会先打印"t2: begin",然后唤醒t1,但是因为锁还没释放,会继续执行t2后面代码,打印"t2: end",最后释放锁,执行t1,打印"Thread-0: end",如果运行结果和我们分析相同,那么等待机制成立。
运行结果:
1.1

1.2 notify随机唤醒

修改运行类:Running.java:

public class Running {public static void main(String[] args) throws InterruptedException {Object lock = new Object();MyThreadA t1 = new MyThreadA(lock);MyThreadA t2 = new MyThreadA(lock);MyThreadA t3 = new MyThreadA(lock);MyThreadB t4 = new MyThreadB(lock);t1.start();t2.start();t3.start();Thread.sleep(1000);t4.start();}
}

多次运行结果如果不相同,则notify唤醒不遵循先来后到。
运行结果:
1.2
1.22

1.3 notifyAll()

修改线程类ThreadB.java:

public class MyThreadB extends Thread{private Object lock;public MyThreadB(Object lock){this.lock = lock;}@Overridepublic void run() {synchronized (lock){System.out.println("t2: begin");lock.notifyAll();//唤醒全部线程
//            lock.notify();//唤醒一个线程,随机的,不释放锁System.out.println("t2: end");}}
}

运行结果:
1.3
很明显唤醒了全部线程。

1.4 join()

有时我们需要等待一个线程运行结束,在执行其他线程,这个时候我们就可以使用join()方法。

public class Test1 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {System.out.println("t1执行");});Thread t2 = new Thread(() -> {System.out.println("t2执行");});t2.start();t2.join();t1.start();}
}

分析代码,t2调用join(),那么只有t2线程执行结束才可以执行下面代码,也就是t1。那么如果是:t2执行,t1执行,那么join成立。
运行结果:
在这里插入图片描述

2. 线程间的状态

线程对象在不同时期有不同状态,这些状态都在枚举类State中,则:

public class MyThreadState {//线程状态,都在State枚举类中public static void main(String[] args) {for (Thread.State state :Thread.State.values()) {System.out.println(state);}}
}

运行结果:
State

NEW:已经安排了工作,但未启动的线程对象。
RUNNABLE:可以运行中的线程对象,可分为正在运行,即将运行。
BLOCKED:受阻塞,等待某个监视锁的线程对象。
WAITING:无期限的等待另一个线程执行特定操作的线程。
TIMED_WIATING:指定等待时间等待另一个线程的操作的线程对象。
TERMINATED:工作结束。

3. 验证线程的状态

3.1 验证NEW、RUNNABLE、TERMINATED

实例化一个线程未启动,这时线程就是NEW状态,当启动后,运行中就是RUNNABLE状态,等待线程运行结束,就是TERMINATED状态。

    //验证: NEW RUNNABLE TERMINATEDpublic static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {System.out.println("运行中: " + Thread.currentThread().getState());});System.out.println("main中: " + t1.getState());Thread.sleep(1000);t1.start();Thread.sleep(1000);System.out.println("运行结束: "  +t1.getState());}

运行结果:
1

3.2 验证WAITING

给一个线程上锁,不解锁,让它一直等待,则线程处于WAITING状态。

    //验证: WAITINGpublic static void main(String[] args) throws InterruptedException {Object lock = new Object();Thread t1 = new Thread(() -> {System.out.println("运行中:");try{synchronized (lock){lock.wait();}}catch (InterruptedException e){e.printStackTrace();}});t1.start();Thread.sleep(1000);System.out.println("无休止等待: " + t1.getState());}

运行结果:
3

3.3 验证TIMED-WAITING

给一个线程休眠,休眠中查看它的状态,则应是TIMED-WAITING状态。

    //验证: TIMED_WAITINGpublic static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {System.out.println("运行中: "  + Thread.currentThread().getState());try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}});t1.start();Thread.sleep(1000);System.out.println("指定时间等待: " + t1.getState());}

运行结果:
5

3.4 验证BLOCKED

当一个线程等待另一个线程而堵塞,则应是BLOCKED状态。

 //验证: BLOCKEDpublic static void main(String[] args) throws InterruptedException {Object lock = new Object();Thread t1 = new Thread(() -> {System.out.println("t1: " + Thread.currentThread().getState());try{synchronized (lock){Thread.sleep(10000);}}catch (InterruptedException e){e.printStackTrace();}});Thread t2 = new Thread(() -> {synchronized (lock){System.out.println("t2: " + Thread.currentThread().getState());}});t1.start();Thread.sleep(1000);t2.start();Thread.sleep(1000);System.out.println("t2: " + t2.getState());}

运行结果:
6

4. 面试题:wait和sleep对比

  1. 一个用于线程通信,一个用于线程堵塞,唯一的相同点就是使线程放弃了一部分执行时间。
  2. wait需要搭配synchronized使用,sleep不用。
  3. wait使Object的方法,sleep使Thread的静态方法。

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

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

相关文章

Linux系统下的zabbix监控平台(单机安装服务)

目录 一、zabbix的基本概述 二、zabbix构成 1.server 2.web页面 3.数据库 4.proxy 5.Agent 三、监控对象 四、zabbix的日常术语 1.主机(host) 2.主机组(host group) 3.监控项(item) 4.触发器(trigger) 5.事件(event) 6.动作(a…

正则判断链接是否为外链,自动加上nofollow标签

网站内容通常会有很多外部链接,如果每个都手动修改nofollow标签很麻烦,这里分享给大家这篇利用正则表达式自动判断链接是否为外链,如果是外部链接就自动加上nofollow标签的教程。这样可以很好的优化网站链接,减少网站传递权重,也无需手动添加修改。 function tin_seo_wl(…

el-dialog无法关闭

代码如下&#xff0c;:visible.sync"result2DeptVisible"来控制dialog的隐显问题&#xff0c;但当点击关闭的时候 &#xff0c;无法关闭&#xff01;&#xff01; <el-dialog :visible.sync"result2DeptVisible" class"el-dialog-view">&…

StarRocks入门到熟练

1、部署 1.1、注意事项 需要根据业务需求设计严谨的集群架构&#xff0c;一般来说&#xff0c;需要注意以下几项&#xff1a; 1.1.1、FE数量及高可用 FE的Follower要求为奇数个&#xff0c;且并不建议部署太多&#xff0c;通常我们推荐部署1个或3个Follower。在三个Followe…

JUC并发编程--------CAS、Atomic原子操作

什么是原子操作&#xff1f;如何实现原子操作&#xff1f; 什么是原子性&#xff1f; 事务的一大特性就是原子性&#xff08;事务具有ACID四大特性&#xff09;&#xff0c;一个事务包含多个操作&#xff0c;这些操作要么全部执行&#xff0c;要么全都不执行 并发里的原子性…

ESP32之LEDC(PWM信号的输出)

一、PWM信号简介 PWM&#xff1a;脉冲宽度调制&#xff0c;简称脉宽调制频率(f)&#xff1a;一秒钟PWM有多少个周期(单位Hz)周期(T)&#xff1a;一个周期的时间占空比(duty)&#xff1a;在一个脉冲周期内&#xff0c;高电平的时间与整个周期时间的比例脉宽时间&#xff1a;一个…

Java学习笔记之----I/O(输入/输出)一

在变量、数组和对象中存储的数据是暂时存在的&#xff0c;程序结束后它们就会丢失。想要永久地存储程序创建的数据&#xff0c;就需要将其保存在磁盘文件中(就是保存在电脑的C盘或D盘中&#xff09;&#xff0c;而只有数据存储起来才可以在其他程序中使用它们。Java的I/O技术可…

机器学习:可解释学习

文章目录 可解释学习为什么需要可解释机器学习可解释还是强模型可解释学习的目标可解释机器学习Local ExplanationGlobal Explanation 可解释学习 神马汉斯&#xff0c;只有在有人看的时候能够答对。 为什么需要可解释机器学习 贷款&#xff0c;医疗需要给出理由&#xff0c;让…

MongoDB 会丢数据吗? 在次补刀MongoDB 双机热备

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis &#xff0c;Oracle ,Oceanbase 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请加微信号 liuaustin3 &#xff08;…

makefile开发应用程序的一个通用模板

下面是一个通用的 Makefile 模板&#xff0c;用于开发 C 语言应用程序&#xff1a; # 编译器设置 CC gcc CFLAGS -Wall -Wextra -stdc99# 可执行文件名 TARGET your_program# 源文件和对象文件 SRCS main.c file1.c file2.c OBJS $(SRCS:.c.o)# 默认目标 all: $(TARGET)#…

【C++】异常处理详解

本篇文章重点将会对C中的异常的相关处理操作进行详解。希望本篇文章的内容会对你有所帮助。 目录 一、C语言的异常处理 二、C异常 2、1 异常概念 2、2 异常的使用 2、3 异常类 2、4 异常的重新抛出 三、异常的安全与规范 3、1 异常的安全 3、2 异常的规范 四、异常的优缺点 &am…

CVE-2023-25157:GeoServer OGC Filter SQL注入漏洞复现

CVE-2023-25157&#xff1a;GeoServer OGC Filter SQL注入漏洞复现 前言 本次测试仅供学习使用&#xff0c;如若非法他用&#xff0c;与本文作者无关&#xff0c;需自行负责&#xff01;&#xff01;&#xff01; 一.GeoServer简介 GeoServer 是用 Java 编写的开源软件服务…

界面控件Telerik UI for WPF——Windows 11主题精简模式提升应用体验

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。Telerik UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visua…

【力扣】416. 分割等和子集 <动态规划、回溯>

【力扣】416. 分割等和子集 给你一个 只包含正整数的非空数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 示例 1&#xff1a; 输入&#xff1a;nums [1,5,11,5] 输出&#xff1a;true 解释&#xff1a;数组可以分割成 [1, 5,…

供热管网安全运行监测,提升供热管网安全性能

城市管网是城市的“生命线”之一&#xff0c;是城市赖以生存和发展的基础&#xff0c;在城市基础设施高质量发展中发挥着重要作用。供热管网作为城市生命线中连接供热管线与热用户的桥梁&#xff0c;担负着向企业和居民用户直接供热的重要职责。随着城市热力需求的急剧增加&…

组相联cache如何快速实现cache line eviction并使用PMU events验证

如何快速实现cache line eviction 一&#xff0c;什么是cache hit、miss、linefill、evict &#xff1f;1.1 如果要程序员分别制造出cache hit、miss、linefill、evict这四种场景&#xff0c;该怎么做&#xff1f; 二&#xff0c;实现cache line eviction的方法1.1 直接填充法3…

C语言关于与运算符

C语言关于&与&&运算符 我们知道&#xff0c;在很多场景中&和&&通常可以相互代替&#xff0c;那么它们到底有什么不同呢&#xff1f; 先看一段代码 bool a, b, c; c a & b;使用clang -S编译出来的指令如下&#xff1a; movb -5(%rbp), %al …

【数据结构】Golang 实现单链表

概念 通过指针将一组零散的内存块串联在一起 &#xff0c; 把内存块称为链表的“结点”。 记录下个结点地址的指针叫作后继指针 next &#xff0c;第一个结点叫作头结点&#xff0c;把最后一个结点叫作尾结点 。 代码实现 定义单链表 在 golang 中可以通过结构体定义单链表…

Android——基本控件(下)(二十)

1. 树型组件&#xff1a;ExpandableListView 1.1 知识点 &#xff08;1&#xff09;掌握树型组件的定义&#xff1b; &#xff08;2&#xff09;可以使用事件对树操作进行监听。 2. 具体内容 既然这个组件可以完成列表的功能&#xff0c;肯定就需要一个可以操作的数据&…

vue从零开始学习

npm install慢解决方法:删掉nodel_modules。 5.0.3:表示安装指定的5.0.3版本 ~5.0.3:表示安装5.0X中最新的版本 ^5.0.3: 表示安装5.x.x中最新的版本。 yarn的优点: 1.速度快,可以并行安装 2.安装版本统一 项目搭建: 安装nodejs查看node版本:node -v安装vue clie : np…