【Java EE初阶 --- 多线程(初阶)】多线程的实现案例

article/2025/7/6 9:31:13

乐观学习,乐观生活,才能不断前进啊!!!

我的主页:optimistic_chen

我的专栏:c语言 ,Java

欢迎大家访问~
创作不易,大佬们点赞鼓励下吧~

文章目录

  • 前言
  • 单例模式
    • 实现单例模式
      • 饿汉模式·
      • 懒汉模式
  • 阻塞队列
    • 生产者消费者模型
    • 标准库中的阻塞队列
  • 模拟实现阻塞队列(生产者消费者模型)
  • 线程池
    • 为什么从线程池去线程更高效?
    • 标准库中的线程池
    • 实现线程池
  • 完结

前言

之前博客对多线程的是什么和基本内容都有详细了解,目前对于多线程的运用还很浅显,不能发挥出多线程应有的实力。这篇博客将带来多线程的基本应用,它会用到什么地方?又会带来什么高效的运行效率?我们又会学到什么?诸位尽情期待…

单例模式

单例模式是设计模式之一
两个新名词,什么是设计模式?什么又是单例模式呢?

设计模式相当于棋谱是行业内大佬为新人撰写的一些高效便捷的思路和案例。棋手除了不断下棋提高实力外,就是尽可能多的研读棋谱,解决问题;而对于程序员来讲,设计模式就是棋手的棋谱,面对一些棘手的问题时,设计模式就是我们的制胜法宝,更加重要的是,设计模式可以大大减少程序员代码风格的不同(因为大家都是根据“棋谱”解决问题)

而单例模式就是众多“棋谱”中的一种,保证某个类在程序中只存在唯⼀⼀份实例(对象), ⽽不会创建出多个实例(只能new一个对象)

实现单例模式

单例模式的实现方式很多,最常见的就是”饿汉”和“懒汉“两种:主要区别就是创建对象的时机:创建对象早,就是饿汉模式(懂得都懂);反之,创建对象迟,就是懒汉模式。

这里一个关键是:构造方法设为 private(使单例模式生效)

饿汉模式·

在初始化(类加载)的时候就new好对象(提早)

class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {//获取实例return instance;//读操作}
}

实例提前创建过,就不涉及线程安全问题,只是一个单纯的读操作。

懒汉模式

使用时再 new 对象(能不创建就不创建)

class Singleton {private static volatile Singleton instance = null;Object locker=new Object();private Singleton() {}public static Singleton getInstance() {//获取实例if (instance == null) {synchronized (locker) {if (instance == null) {//当instance为空,创建实例instance = new Singleton();}}}return instance;}
}

在这里插入图片描述

阻塞队列

阻塞队列能是⼀种线程安全的数据结构,也遵守“先进先出”的基本原则,并且具有以下特性:
• 天然线程安全
• 当队列满的时候, 继续⼊队列就会阻塞, 直到有其他线程从队列中取⾛元素.
• 当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插⼊元素.

最主要的应用场合是 生产者消费者模型

生产者消费者模型

<举个例子> 过年包饺子,我负责擀饺子皮,妈妈和爸爸负责包饺子,持续一段时间后,整个工作结束。整个场景的资源是“饺子皮”,我是“生产者”,爸爸妈妈是“消费者”,这个场景就是“阻塞队列”

生产者消费者模型的优势:

  1. 解耦合
    在这里插入图片描述
  2. 削峰填谷
    在这里插入图片描述

生产者消费者模型要付出的代价:
1. 整体结果趋于复杂化,需要更多机器,生产环境难以管理
2. 影响效率

标准库中的阻塞队列

public interface BlockingQueue<E> extends Queue<E>{
}

BlockingQueue 是⼀个接⼝.所以不能直接去new, 其真正实现的类是 LinkedBlockingQueue.
在这里插入图片描述
本来,队列入队和出队操作是offer和poll,但是我们这里是多线程情况,得使用带有阻塞功能的put和take。

比如:当队列为空时,却被take时,就会出现阻塞
在这里插入图片描述
这是完美运行的生产者消费者模型:

 public static void main(String[] args) throws InterruptedException {BlockingQueue<Integer> queue=new LinkedBlockingQueue<>(1000);Thread producer=new Thread(()->{int n=0;while(true){try {queue.put(n);n++;System.out.println("生产元素"+n);} catch (InterruptedException e) {e.printStackTrace();}}},"producer");Thread consumer=new Thread(()->{while(true){try {Integer n=queue.take();System.out.println("消费元素"+n);} catch (InterruptedException e) {e.printStackTrace();}}},"consumer");producer.start();consumer.start();}

在这里插入图片描述

模拟实现阻塞队列(生产者消费者模型)

import java.util.concurrent.BlockingQueue;class MyBlockQueue{private String[] data=null;private int head;private int tail;private int size;public MyBlockQueue(int capacity){data=new String[capacity];}public void put(String elem) throws InterruptedException {synchronized (this){while(size>=data.length){//队列满了,阻塞this.wait();//此时阻塞,当队列不满时,唤醒}data[tail]=elem;tail++;if(tail>=data.length){tail=0;}size++;this.notify();}}public String take() throws InterruptedException {synchronized (this){while(size==0){//while二次判定队列是否为空//队列为空,阻塞this.wait();//队列不空时,唤醒}String ret=data[head];head++;if(head>=data.length){head=0;}size--;this.notify();return ret;}}
}public class Demo12 {public static void main(String[] args) {MyBlockQueue queue=new MyBlockQueue(1000);Thread producer=new Thread(()->{int n=0;while(true){try {queue.put(n+"");n++;System.out.println("生产元素"+n);} catch (InterruptedException e) {e.printStackTrace();}}},"producer");Thread consumer=new Thread(()->{while(true){try {String  n=queue.take();System.out.println("消费元素"+n);} catch (InterruptedException e) {e.printStackTrace();}}},"consumer");producer.start();consumer.start();}
}

线程池

对比常量池来看,字符串常量,在Java程序运行最初的时候就构建好了,等程序运行的时候,这些常量就加载到内存中;后续使用这些字符串时,直接从内存中获取,避免了字符串构造/销毁的开销。
线程池也是这样,提前创建好要使用的线程,以后要用的时候就能减少每次创建、销毁线程的损耗,提高性能。

<举个例子>其实大家可以带入“鱼塘”,想吃鱼了,捞一条上来。鱼塘里很多鱼,减少了外出购买的时间,是不是降低了损耗...

为什么从线程池去线程更高效?

首先,一个操作系统是它的内核与配套的应用程序组成。内核给应用程序提供稳定的运行环境,同时管理硬件设备。
创建线程需要操作系统内核的配合,但是内核只有一个,如果同时出现多个要求创建线程的需求,这个时候还能保证高效吗?但是如果从线程池中取现成的线程,就不需要内核的参与,就不会出现“拥挤”的情况

<举个例子>使用线程池就是去银行ATM机上取钱,创建线程是去银行柜台取钱。生活中,我们都知道ATM的效率高。

所以,使用线程池就可以省下应用程序切换到内核运行这样的消耗。

标准库中的线程池

核心方法:submit(Runnable)
通过Runnnable描述一段要执行的任务,通过submit把任务放到线程池中,此时线程池里的线程就会执行任务。本质上还是生产者消费者模型,submit在生产任务,线程池在消费任务。
在这里插入图片描述

构造这个类时,构造方法比较复杂,它的参数特别多…
主要看参数最多的构造方法,掌握整个其他的也就拿下了。

在这里插入图片描述

BlockingQueue workQueue:线程池允许我们程序员自己传一个工作队列,这可操作性就很高,我们可自主选择队列的基本参数,包括它的底层实现。
ThreadFactory threadFactory:创建线程的工厂,设计模式的一种,和前面的单例模式属于并列关系。为了统一构造并初始化线程。
在这里插入图片描述

工厂方法的核心:通过静态方法,把构造对象new的过程、各种初始化的过程,都封装为一个方法;提供多组静态方法,实现不同情况的构造。提供工厂方法的类就是工厂类。
在这里插入图片描述

RejectedExecutionHandler handler:拒绝策略,入队时,队列满了,不会真的造成阻塞(拒绝等待),而是执行下面拒绝策略的代码(提高效率)
在这里插入图片描述

实现线程池

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;class MyThreadPool{private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}public MyThreadPool(int n){for(int i = 0; i<n; i++){Thread t=new Thread(()->{while(true){try {Runnable runnable=queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}}
}public class Demo14 {public static void main(String[] args) throws InterruptedException {MyThreadPool pool=new MyThreadPool(4);for(int i=0;i<100;i++){pool.submit(()->{System.out.println("hello "+Thread.currentThread().getName());});}}
}

完结


可以点一个免费的赞并收藏起来~
可以点点关注,避免找不到我~ ,我的主页:optimistic_chen
我们下期不见不散 ~ ~ ~


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

相关文章

BiliNote部署实践

​ 开源地址&#xff1a; https://github.com/JefferyHcool/BiliNote &#x1f680; 快速开始 1. 克隆仓库 git clone https://github.com/JefferyHcool/BiliNote.git cd BiliNote mv .env.example .env2. 启动后端&#xff08;FastAPI&#xff09; cd backend pip insta…

基于回归算法的心理健康预测(EDA + 预测)

心理健康涵盖情感、心理与社会福祉&#xff0c;影响认知、情绪和行为模式&#xff0c;决定压力应对、人际交往及健康决策&#xff0c;且在生命各阶段&#xff08;从童年至成年&#xff09;均至关重要。心理健康与身体健康同为整体健康的核心要素&#xff1a;抑郁会增加糖尿病、…

无他相机:专业摄影,触手可及

在数字摄影时代&#xff0c;手机摄影已成为许多人记录生活、表达创意的重要方式。无他相机正是这样一款专为摄影爱好者设计的相机应用程序&#xff0c;它不仅提供了专业级摄影设备的大部分功能&#xff0c;还通过简洁直观的操作界面&#xff0c;让每一位用户都能轻松上手&#…

Qt/C++编写GB28181服务端工具/绿色版开箱即用/对标wvp-gb28181/实时画面预览/录像回放下载

一、前言说明 使用过不少的gb28181服务端工具&#xff0c;绝大部分都是BS结构的&#xff0c;也就是直接在网页上运行&#xff0c;比如easynvr、liveqing等&#xff0c;也有个知名的开源国标项目叫wvp&#xff0c;总体感觉性能都不如意&#xff0c;理论上来说肯定不如直接CS结构…

ProxyPin抓APK数据包

ProxyPin抓APK数据包&#xff1a; 下载地址&#xff1a;https://github.com/wanghongenpin/proxypin 环境配置&#xff1a; 夜神模拟器&#xff0c;开心消消乐&#xff0c;ProxyPin.apk&#xff0c;ProxyPin.exe 使用步骤&#xff1a; ProxyPin.apk安装https证书&#xff0…

空间智能重塑未来治理

当上海复兴岛的战略空间更新与科创策源功能在时空创新实验基地实现耦合共生&#xff0c;当武汉马拉松的智慧安防系统通过"实景三维城市智眼"构筑起全域智能防线&#xff0c;我们正见证着空间智能技术重构城市治理范式的革命性变革。这场以数据为血脉、算法为神经、模…

FastAPI安全认证:从密码到令牌的魔法之旅

title: FastAPI安全认证:从密码到令牌的魔法之旅 date: 2025/06/02 13:24:43 updated: 2025/06/02 13:24:43 author: cmdragon excerpt: 在FastAPI中实现OAuth2密码流程的认证机制。通过创建令牌端点,用户可以使用用户名和密码获取JWT访问令牌。代码示例展示了如何使用Cry…

Java Script函数

1.认识JS函数 1.1程序中的foo、bar、baz 在学习编程中&#xff0c;你可能会经常看到foo、bar、baz这些名词 它们通常被用来作为函数、变量、文件的名称 目前已经编程了计算机编程的术语一部分 但是它们本身并没有特别的用途和意义 常常被称之为“伪变量”&#xff08;metasynt…

吴恩达MCP课程(5):mcp_chatbot_prompt_resource.py

前提条件&#xff1a; 1、吴恩达MCP课程&#xff08;5&#xff09;&#xff1a;research_server_prompt_resource.py 2、server_config_prompt_resource.json文件 {"mcpServers": {"filesystem": {"command": "npx","args"…

性能测试的概念和场景设计

一、什么是性能 性能的定义&#xff1a;性能是指系统或设备在执行特定任务时的时间效率和资源利用情况。可以从两个主要维度来理解&#xff1a; 时间维度&#xff1a; a、响应时间&#xff1a;指系统从接收用户请求到返回结果所需的时间。 b、吞吐量&#xff1a;指单位时间内…

【Java学习笔记】异常

异常&#xff08;Exception&#xff09; 一、基本介绍 在 Java 程序中&#xff0c;将运行中发生的不正常情况称为 “异常”&#xff0c;开发过程中的语法错误和运行时发生的异常情况是不一样的。 二、异常的分类 1. Error&#xff08;错误&#xff09;&#xff1a;Java 虚拟…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Form Wave(表单label波动效果)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— FormWave组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 组件目标 构建一个美观、动态的登录表单&#xff0…

数据库系统概论(十六)数据库安全性(安全标准,控制,视图机制,审计与数据加密)

数据库系统概论&#xff08;十六&#xff09;数据库安全性 前言一、数据库安全性1. 什么是数据库安全性&#xff1f;2. 为何会存在安全问题&#xff1f; 二、安全标准的发展1. 早期的“开拓者”&#xff1a;TCSEC标准2. 走向国际统一&#xff1a;CC标准3. TCSEC和CC标准有什么不…

显示即战略:铁电液晶如何成为 “数字中国” 的 “像素基石”?

一、显示技术&#xff1a;数字时代的核心战略支点 &#xff08;一&#xff09;从 “视觉窗口” 到 “战略基础设施” 在数字经济蓬勃发展的当下&#xff0c;显示技术早已超越了单纯的 “视觉呈现” 范畴&#xff0c;成为连接人与数字世界的关键接口。从智能手机、平板电脑到车…

⚡️ Linux grep 命令参数详解

⚡️ Linux grep 用法及参数详解 &#x1f4d8; 1. grep 简介 grep 是 Linux/Unix 系统中用于文本搜索的命令&#xff0c;其全称为 Global Regular Expression Print&#xff0c;意为全局正则表达式打印器。 它根据给定的 模式&#xff08;pattern&#xff09; 对文件或标准…

Rust 变量与可变性

文章目录 变量与可变性常量遮蔽&#xff08;Shadowing&#xff09; 变量与可变性 Rust中变量默认是不可变的&#xff0c;这是 Rust 鼓励你编写更安全、易于并发代码的众多方式之一。不过&#xff0c;你仍然可以选择让变量可变。让我们来探讨 Rust 为什么鼓励你优先使用不可变性…

YOLOV7改进之融合深浅下采样模块(DSD Module)和轻量特征融合模块(LFI Module)

目录 一、研究背景​ 二. 核心创新点​ ​2.1 避免高MAC操作​ ​2.2 DSDM-LFIM主干网络​ 2.3 P2小目标检测分支​ ​3. 代码复现指南​ 环境配置 关键修改点 ​4. 实验结果对比​ 4.1 VisDrone数据集性能 4.2 边缘设备部署 4.3 检测效果可视化 ​5. 应用场景​ …

(Python网络爬虫);抓取B站404页面小漫画

目录 一. 分析网页 二. 准备工作 三. 实现爬虫 1. 抓取工作 2. 分析工作 3. 拼接主函数&运行结果 四. 完整代码清单 1.多线程版本spider.py&#xff1a; 2.异步版本async_spider.py&#xff1a; 经常逛B站的同志们可能知道&#xff0c;B站的404页面做得别具匠心&…

Qt OpenGL 3D 编程入门

Qt 提供了强大的 OpenGL 集成功能&#xff0c;使得在 Qt 应用中实现 3D 图形变得更加简单。以下是使用 Qt 进行 OpenGL 3D 编程的基础知识。 1. 环境配置 创建 Qt 项目 新建 Qt Widgets Application 项目 在 .pro 文件中添加 OpenGL 模块&#xff1a; qmake QT co…

山东警方:10岁失联男童在河道溺亡 搜救行动告终

6月2日,山东滕州市公安局发布警情通报。5月31日22时35分许,警方接到孔某某报警,称其10岁的外孙赵某某于当天17时许离家后失联。接警后,公安机关迅速调阅监控、走访群众,并联合当地政府和社会救援力量,使用搜救警犬和无人机持续开展搜寻工作。6月2日15时许,在邻村一河道内…