Java—— 多线程 第二期

article/2025/7/19 8:07:55

等待唤醒机制(生产者和消费者)

说明 

之前的多线程是谁抢到CPU的执行权谁执行,而等待唤醒机制作为一种经典的多线程协作模式,可以实现线程的交替执行。

成员

实现等待唤醒机制需要三个成员:生产者、消费者、标志位

可以分别看作厨师、吃货、桌子

生产者(厨师)需要做的事情:

1.判断桌子上是否有食物

2.有:等待

3.没有:制作食物

4.把食物放在桌子上(修改食物状态为有食物)

5.叫醒等待的消费者开吃

消费者(吃货)需要做的事情:
1.判断桌子上是否有食物
2.没有:等待
3.有:开吃
4.吃食物(修改食物状态为没有食物)

5.叫醒等待的生产者开吃继续做

标志位(桌子)需要做的事情:

标志是否有食物

常见方法

方法名称说明
void wait()当前线程等待,直到被其他线程唤醒(底层会退出 synchronized 块,让其他线程可以获取锁)
void notify( )随机唤醒单个线程
void notifyAll()唤醒所有线程

代码演示

标志类
public class Table {//标志位,0表示桌子上没有食物,1表示有public static int flag = 0;//表示线程交替执行5次public static int count = 5;//表示锁对象public static Object lock = new Object();}
生产者类
public class Cook extends Thread {@Overridepublic void run() {//循环while (true) {//同步代码块synchronized (Table.lock) {//判断是否执行到了末尾if (Table.count == 0) {return;} else {//1.判断桌子上是否有食物if (Table.flag == 1) {//2.有:等待try {//用锁对象调用等待方法,底层会将线程与锁绑定//这样后续用锁对象调用唤醒方法,就会把与锁对象绑定的所有线程唤醒Table.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {//3.没有:制作食物System.out.println("厨师做了一碗面条");//4.把食物放在桌子上(修改食物状态为有食物)Table.flag = 1;//5.叫醒等待的消费者开吃Table.lock.notifyAll();}}}}}
}
消费者类
public class Foodie extends Thread {@Overridepublic void run() {//循环while (true) {//同步代码块synchronized (Table.lock) {//判断是否执行到了末尾if (Table.count == 0) {return;} else {//1.判断桌子上是否有食物if (Table.flag == 0) {//2.没有:等待try {Table.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {//3.有:开吃Table.count--;System.out.println("吃货吃了一碗面条,还能吃" + Table.count + "碗");//4.吃食物(修改食物状态为没有食物)Table.flag = 0;//5.叫醒等待的生产者开吃继续做Table.lock.notifyAll();}}}}}
}
实现类
public class Test {public static void main(String[] args) {//生产者线程Cook c = new Cook();//消费者线程Foodie f = new Foodie();//开启线程c.start();f.start();}
}
结果展示 


阻塞队列

说明

阻塞队列常用于生产者和消费者的场景,生产者往队列里放元素,消费者从队列里拿元素

当队列为空时,获取元素的消费者线程会等待,直到队列变为非空时,继续获取元素

当队列满时,添加元素的生产者线程会等待,直到队列出现空位,继续添加元素

可以设定阻塞队列的长度,当长度为一时,放一个拿一个,也能实现线程的交替执行

阻塞队列实现类

ArrayBlockingQueue(底层是数组,有界)

LinkedBlockingQueue(底层是链表,无界,但不是真正的无界,最大为int的最大值)

实现类实现了Queue,BlockingQueue,Iterable,Collection接口,表示其可以用迭代器遍历,也是单列集合的一种。

常用方法

方法名说明
public void put(E e)生产者方法,往阻塞队列里添加元素
public E take()消费者方法,得到阻塞队列里的元素

put方法底层会获取阻塞队列的锁对象,进行上锁,往阻塞队列里添加元素,如果阻塞队列满了就等待,最后进行解锁

take方法底层会获取阻塞队列的锁对象,进行上锁,获取阻塞队列里的元素,如果没有元素就等待,最后进行解锁

因为两个方法底层的锁对象都是和阻塞队列挂钩的,使用同一个阻塞队列,实现锁的唯一性

代码演示

生产者类
import java.util.concurrent.ArrayBlockingQueue;public class Cook extends Thread{private ArrayBlockingQueue<String> queue;//构造方法,得到传递过来的阻塞队列public Cook(ArrayBlockingQueue<String> queue){this.queue = queue;}@Overridepublic void run() {while(true){//不断往阻塞队列里添加元素try {queue.put("食物");System.out.println("厨师做了一碗食物");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
消费者类
import java.util.concurrent.ArrayBlockingQueue;public class Foodie extends Thread{private ArrayBlockingQueue<String> queue;//构造方法,得到传递过来的阻塞队列public Foodie(ArrayBlockingQueue<String> queue){this.queue = queue;}@Overridepublic void run() {while(true){//不断拿元素try {String take = queue.take();System.out.println(take);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
实现类
import java.util.concurrent.ArrayBlockingQueue;public class Test {public static void main(String[] args) {//创建阻塞队列,设定队列长度为1ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);//创建线程,传递同一个阻塞队列,实现使用同一个阻塞队列//传递运行次数Cook c = new Cook(queue);Foodie f = new Foodie(queue);c.start();f.start();}
}
结果展示

因为put方法,take方法底层有锁,则打印语句写在了锁外面,会出现连续打印的情况,但数据是在锁中的,对实际数据不会有影响,只影响了输出效果

线程的六种状态

新建状态(NEW)                        创建线程对象
就绪状态(RUNNABLE)             start方法
阻塞状态(BLOCKED)               无法获得锁对象
等待状态(WAITING)                 wait方法
计时等待(TIMED WAITING)     sleep方法
结束状态(TERMINATED )        全部代码运行完毕

线程池

说明

之前在写多线程的时候,用到多线程就创建,用完就销毁,这样会增加运行时间和浪费资源

所以可以使用线程池,实现线程的复用

主要核心原理

创建一个池子,池子中是空的(可设定池子中最多能有几条线程)
提交任务时,池子会创建线程对象,任务执行完毕,线程归还给池子
下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
但若提交任务时,池中没有空闲线程,也无法创建新的线程(已达上限),任务就会排队等待

线程池代码实现

创建线程池

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。

方法名称说明
public static ExecutorService newCachedThreadPool()创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool(int n)创建有上限的线程池
提交任务
方法名称说明
public Future<?> submit提交任务
所有的任务全部执行完毕,关闭线程池
方法名称说明
public void shutdown()关闭线程池,一般不需要关闭
代码演示

任务类

public class MyRun implements Runnable {@Overridepublic void run() {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
}

实现类

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Test {public static void main(String[] args) {//创建线程池,设定上限为3ExecutorService e = Executors.newFixedThreadPool(3);//创建任务对象MyRun mr = new MyRun();//提交任务e.submit(mr);e.submit(mr);e.submit(mr);e.submit(mr);e.submit(mr);//关闭任务e.shutdown();}
}

 结果部分展示

因为线程池设定上限为3,所以即使提交了5个任务,也只创建了3个线程,并复用这3个线程

自定义线程池

创建自定义线程池:

ThreadPoolExecutor pool = new ThreadPoolExecutor(......); 

传递六个参数:

核心线程数量(不能小于0)
线程池中最大线程的数量(最大数量>= 核心线程数量)
空闲时间(值)(不能小于0)
空闲时间(单位)(用TimeUnit指定)
阻塞队列(不能为null)
创建线程的方式(不能为null)
要执行的任务过多时的任务拒绝策略(不能为null) 

TimeUnit时间单位说明
TimeUnit.SECONDS单位秒
TimeUnit.MINUTES单位分钟
阻塞队列说明
new ArrayBlockingQueue<>(...)创建数组线程池,传递线程池长度
new LinkedBlockingQueue<>()创建链表线程池
创建线程的方式说明
Executors.defaultThreadFactory()利用线程池工具类,底层创建线程对象,并对线程对象进行一系列的设置
任务拒绝策略说明
ThreadPoolExecutor.AbortPolicy

最常用也是默认策略,丢弃任务并抛出 RejectedExecutionException异常 

AbortPolicy是一个静态内部类

用外部类.内部类创建该对象

不断的提交任务时,核心线程,阻塞队列,临时线程的使用时机:
当核心线程满时,再提交任务就会排队
当核心线程满,队列满时,会创建临时线程
当核心线程满,队列满,临时线程满时,会触发任务拒绝策略

代码演示

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Test {public static void main(String[] args) {//创建自定义线程池,传递六个参数ThreadPoolExecutor pool = new ThreadPoolExecutor(3,6,60,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());//提交任务pool.submit(new MyRun());pool.submit(new MyRun());pool.submit(new MyRun());}
}

线程池多大合适

CPU密集型运算:最大并行数+1

 I/O密集型运算:

最大并行数 x 期望CPU利用率 x [(CPU计算时间 + 等待时间) / CPU 计算时间]


http://www.hkcw.cn/article/Rsqtdrdxjc.shtml

相关文章

2025年最新《Python程序设计》题库(含答案)

判断题填空题选择题程序题 点击文末名片可以下载python工具和完整题库&#xff01; 第 1 章 基础知识 &#xff08;部分展示&#xff09; 1、 Python 是一种跨平台、开源、免费的高级动态编程语言。 2、 Python 3.x 完全兼容 Python 2.x。 3、 Python 3.x 和 Python 2.x 唯…

【AI非常道】二零二五年五月,AI非常道

经常在社区看到一些非常有启发或者有收获的话语&#xff0c;但是&#xff0c;往往看过就成为过眼云烟&#xff0c;有时再想去找又找不到。索性&#xff0c;今年开始&#xff0c;看到好的言语&#xff0c;就记录下来&#xff0c;一月一发布&#xff0c;亦供大家参考。 前面的记…

Linux入门(十一)进程管理

Linux 中每个执行的程序都称为一个进程&#xff0c;每个进程都分配一个ID号&#xff08;PID&#xff09; 每个进程都可能以两种方式存在&#xff0c;前台&#xff08;屏幕上可以操作的&#xff09;和后台&#xff08;屏幕上无法看到的&#xff09;&#xff0c;一般系统的服务都…

晨控CK-UR12与西门子PLC配置Modbus TCP通讯连接操作手册

晨控CK-UR12与西门子PLC配置Modbus TCP通讯连接操作手册 晨控CK-UR12系列作为晨控智能工业级别RFID读写器,支持大部分工业协议如RS232、RS485、以太网。支持工业协议Modbus RTU、Modbus TCP、Profinet、EtherNet/lP、EtherCat以及自由协议TCP/IP等。 本期主题&#xff1a;围绕…

Python使用

Python学习&#xff0c;从安装&#xff0c;到简单应用 前言 Python作为胶水语言在web开发&#xff0c;数据分析&#xff0c;网络爬虫等方向有着广泛的应用 一、Python入门 相关基础语法直接使用相关测试代码 Python编译器版本使用3以后&#xff0c;安装参考其他教程&#xf…

高德地图应用OceanBase单元化构建下一代在线地图服务

IEEE International Conference on Data Engineering (ICDE) 是数据库和数据工程领域的顶级学术会议之一&#xff08;与SIGMOD、VLDB并成为数据库三大顶会&#xff09;&#xff0c;自1984年首次举办以来&#xff0c;每年举办一次。ICDE涵盖广泛的主题&#xff0c;包括数据库系统…

软考-系统架构设计师-第十九章 嵌入式系统架构设计理论与实践

嵌入式系统架构设计理论与实践 19.1 嵌入式系统发展历程19.2 嵌入式系统硬件19.3 嵌入式系统软件19.4 嵌入式系统软件架构设计方法19.5 嵌入式系统软件架构实践 19.1 嵌入式系统发展历程 发展历程硬件软件主要特点单片微型计算机&#xff08;SCM&#xff09;单片机无操作系统 …

DeepSeek-R1 重磅升级,智能体验再进化!

DeepSeek AI 爱好者们注意啦&#xff01;DeepSeek R1 模型完成小版本升级&#xff0c;新版本 DeepSeek-R1-0528 震撼登场。想体验超强思考与推理能力&#xff1f;官方网站、APP、小程序&#xff0c;一键开启 “深度思考” 功能&#xff0c;新版等你来探索&#xff01;API 也同步…

预处理深入详解:预定义符号、宏、命名约定、命令行定义、条件编译、头文件的包含

目录 一、预定义符号 二、#define定义常量 三、宏 &#xff08;一&#xff09;#define定义宏 &#xff08;二&#xff09;带有副作用的宏参数 &#xff08;三&#xff09;宏替换的规则 &#xff08;四&#xff09;宏和函数的对比 四、#和## &#xff08;一&#xff09…

深度解析:跨学科论文 +“概念迁移表” 模板写作全流程

跨学科论文速通&#xff01;融合“概念迁移表”的写作导航模板 你的论文是否曾被导师皱眉评价为“四不像”&#xff1f;不同学科的术语在稿纸上打架&#xff0c;核心逻辑若隐若现&#xff1f; 别让心血沦为学术混搭的牺牲品。一张精心设计的 概念迁移表&#xff0c;能将两个看…

Linux安装及管理程序

1 Linux应用程序基础 1.1 Linux 命令与应用程序的关系 在 Linux 操作系统中&#xff0c;一直以来命令和应用程序并没有特别明确的区别&#xff0c;从长期使用习惯来看&#xff0c;可以通过以下描述来对两者进行区别&#xff1a; 应用程序命令的执行文件大多比较小&#xff0…

历年南京大学计算机保研上机真题

2025南京大学计算机保研上机真题 2024南京大学计算机保研上机真题 2023南京大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school Count Number of Binary Strings 题目描述 Given a positive integer n n n ( 3 ≤ n ≤ 90 3 \leq n \leq 90 3≤n≤…

酒店管理系统设计与实现

本科毕业设计(论文) 设计(论文)题目 酒店管理系统设计与实现 学生姓名 学生学号 所在学院 专业班级 校内指导教师 李建 企业指导教师 毕业设计(论文)真实性承诺及声明 学生对毕业设计(论文)真实性承诺 本人郑重声明:所提交的毕业设计(论文)作品是本人在指导教师的指…

Java web学习路径预览

Java web学习路径预览 &#xff08;图源&#xff1a;黑马程序员&#xff09; 目录 Java web学习路径预览 一、HTML、CSS、JS 1. HTML (HyperText Markup Language): 网页的骨架 2. CSS (Cascading Style Sheets): 网页的皮肤 3. JavaScript (JS): 网页的行为 二、Ajax、…

QEMU/KVM课程大纲暨学习路线(1)

一、背景 去年(2024年)10月份,在CSDN上有一位网友联系到我,说有需要我帮忙的地方。加了微信之后,他说了要帮助的事情。原来是他看到了我的QEMU/KVM相关文章,阅读之后觉得符合他们的要求,所以想让我帮他们开发QEMU/KVM相关的课程。 经过沟通和了解,他们之前请了一位老师…

得物前端面试题及参考答案(精选50道题)

浏览器强制缓存和协商缓存的机制及区别 浏览器缓存机制用于减少网络请求、提升页面加载性能&#xff0c;强制缓存和协商缓存是其中两种核心策略。 强制缓存的机制&#xff1a;当浏览器请求资源时&#xff0c;首先检查该资源在本地缓存中的有效期。有效期由响应头中的Cache-Con…

动态IP与区块链:重构网络信任的底层革命

在数字经济蓬勃发展的今天&#xff0c;网络安全与数据隐私正面临前所未有的挑战。动态IP技术与区块链的深度融合&#xff0c;正在构建一个去中心化、高可信的网络基础设施&#xff0c;为Web3.0时代的到来奠定基础。 一、技术碰撞&#xff1a;动态IP与区块链的天然契合 动态I…

PCB设计实践(三十)地平面完整性

在高速数字电路和混合信号系统设计中&#xff0c;地平面完整性是决定PCB性能的核心要素之一。本文将从电磁场理论、信号完整性、电源分配系统等多个维度深入剖析地平面设计的关键要点&#xff0c;并提出系统性解决方案。 一、地平面完整性的电磁理论基础 电流回流路径分析 在PC…

使用vscode进行c/c++开发的时候,输出报错乱码、cpp文件本身乱码的问题解决

使用vscode进行c/c开发的时候&#xff0c;输出报错乱码、cpp文件本身乱码的问题解决 问题描述解决方案问题1的解决方案问题2解决方案 问题描述 本篇文章解决两个问题&#xff1a; 1.当cpp文件出现错误的时候&#xff0c;编译时报错&#xff0c;但是报错内容缺是乱码&#xff0…

信息化项目验收测试:MES 系统验收测试的测试重点

在工业4.0与智能制造转型中&#xff0c;MES系统作为连接计划层与执行层的枢纽&#xff0c;其验收测试的专业性直接影响企业数字化成效。第三方检测机构需从核心功能、性能、集成能力等维度&#xff0c;为企业提供科学的验收测试方案。 一、核心功能验证&#xff1a;打通生产执行…