当前位置: 首页 > news >正文

《Java-SE-第二十六章》之线程池

前言

在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!”

博客主页:KC老衲爱尼姑的博客主页

博主的github,平常所写代码皆在于此

共勉:talk is cheap, show me the code

作者是爪哇岛的新手,水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!


文章目录

  • 线程池概述
    • 什么是线程池?
      • 为什么从线程池拿会比直接创建线程快?
        • Java标准库中的线程池
          • 四种拒接策略演示
      • Executors
      • 简单实现线程池
        • 线程池的基本逻辑
        • 实现线程池的基本逻辑

线程池概述

什么是线程池?

  线程虽然是轻量级进程,尽管线程比进程创建和销毁所消耗 的资源要少。但是如果线程的创建和销毁频率高了,开销也还是有的,为了进一步提高效率,引入了线程池,池子里面放着事先创建好的线程.后面用的时候直接从池子里面拿,如此速度就快了,但是代价线程池所需的空间,线程池就是以空间换时间。

为什么从线程池拿会比直接创建线程快?

  因为创建线程和销毁线程是操作系统完成了,需要从用户态切换到内核态 这是耗时耗力 的。如果从线程池直接拿的话,就省去了切换到内核态的时间,同时当线程不用的时候直接放回到线程池即可。

Java标准库中的线程池

  标准库中线程池为ThreadPoolExecutor类,该类中最主要是包含两类线程,一类是核心线程,另一类是非核心线程。当派发任务给线程池中的线程时,干活的是核心线程,当来的活太多了,核心线程不够用了,就会启动非核心线程。当活变少了,就会把非核心线程 给裁了。简单来说所谓的核心线程就像公司里面的正式工,非核心线程则是实习生。当公司人手不够的时候就会招多点实习生来干活,当活少了,实习生也就可以走了。

  在Java8中,ThreadPoolExecutor一共提供了4个构造方法,在此主要介绍参数最多的,其他的三个构造方法都是这个构造方法减少参数而来的,所以搞懂了这个参数最多的构造方法,其他的自然而然也明白了。

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

参数解释

  • corePoolSize:表示核心线程数
  • maximumPoolSize:池中允许的最大线程数,就是核心线程和非核心线程之和
  • keepAliveTime:非核心线程在被终止之前等待新任务的最大时间,超过这个时间,该线程就会被停用。
  • unit:时间单位
  • workQueue:在任务执行之前用于保存任务的队列,该队列仅将保存submit方法提交的Runnable任务
  • threadFactory:创建新线程 时所使用的工厂
  • RejectedExecutionHandler:拒绝策略,执行被处理使用的处理程序,因为达到线程限制和对列容量

拒接策略详解

ThreadPoolExecutor中有四个静态内部类实现了RejectedExecutionHandler接口,分别对应四种不同的拒绝策略

  • AbortPolicy:被拒绝的任务的处理程序,抛出一个 RejectedExecutionException 。当活太多了,线程已经忙不过来了,还来活时,直接不处理,抛出异常。
  • CallerRunsPolicy:任务从哪里来就回到哪里去。
  • DiscardOldestPolicy:队列满了但是不会抛出异常,直接丢弃新任务,不做任何处理
  • DiscardPolicy:队列满了, 丢弃工作队列中最旧的任务,然后尝试再次提交新任务,不会抛出异常。

常用方法

方法解释
submit()用于提交带有返回值的任务(Callable)和不带返回值的任务(Runnable)。
execute()方法用于提交不带返回值的任务(Runnable)方法没有返回值,因此无法获取任务的执行结果或处理任务的异常。如果任务执行抛出异常,将会被线程池内部捕获并记录日志。
shutdown()优雅地关闭线程池,避免资源泄漏和线程阻塞问题。停止接受新的任务,并等待已提交的任务执行完成
四种拒接策略演示

AbortPolicy

演示代码

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(1,1,3,TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 5; i++) {final int taskId = i;threadExecutor.submit(()->someTask(taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(int taskId) {System.out.println("Task " + taskId + " is starting...");try {Thread.sleep(100); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}

运行结果:
在这里插入图片描述

任务太多了,抛出异常之后就罢工了,不干活了。

CallerRunsPolicy

演示代码

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(1,1,3,TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 5; i++) {final int taskId = i;threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(100); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}

运行结果:

在这里插入图片描述

 当任务过多时,直接拒接不干了,要干你自己干,所以有部分任务是main线程自己干的

DiscardOldestPolicy

代码演示


import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(1,1,3,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 0; i < 5; i++) {final int taskId = i;threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(100); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}

运行结果:

在这里插入图片描述

DiscardPolicy

代码演示

import java.util.concurrent.*;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(2,2,3,TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());for (int i = 0; i < 10; i++) {final int taskId = i;threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(2000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}

运行结果:
在这里插入图片描述

Executors

  如果你觉得上述创建线程池的方式太复杂了,可以使用Executors来创建线程,其返回值是ExecutorService接口。Executors 本质上是 ThreadPoolExecutor 类的封装.

Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池

  • newCachedThreadPool: 创建线程数目动态增长的线程池.

  • newSingleThreadExecutor: 创建只包含单个线程的线程池.

  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.

使用演示


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorsDemo {public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {final int taskId = i;executorService.submit(()->someTask(Thread.currentThread().getName(),taskId));}executorService.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(2000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}

运行结果:

在这里插入图片描述

简单实现线程池

线程池的基本逻辑

  线程池事先存放着准备好的线程,当有任务提交入池的时候,实际上是放入了阻塞队列中,然后线程池中的线程调度执行这些任务,在java中的线程池有核心线程和非核心线程,我们是简单实现,所以都是以核心线程的方式实现。

实现线程池的基本逻辑

  使用阻塞队列组织所有的任务,定义一个线程池类其核心方法为submit()将任务添加到阻塞队列中,还需要一个工作线程不断向阻塞对列扫描获取任务并执行任务。

实现代码

MyThreadPool类实现

import java.util.concurrent.LinkedBlockingQueue;public class MyThreadPool {private int maxWorkerCount = 10;private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue();public void submit(Runnable command) throws InterruptedException {if (queue.size() < maxWorkerCount) {// 当前 worker 数不足, 就继续创建 workerWorker worker = new Worker(queue);worker.start();}// 将任务添加到任务队列中queue.put(command);}}

Worker实现


import java.util.concurrent.LinkedBlockingQueue;public class Worker extends Thread {private LinkedBlockingQueue<Runnable> queue = null;public Worker(LinkedBlockingQueue<Runnable> queue) {super("worker");this.queue = queue;}@Overridepublic void run() {// try 必须放在 while 外头, 或者 while 里头应该影响不大try {while (!Thread.interrupted()) {Runnable runnable = queue.take();runnable.run();}} catch (InterruptedException e) {}}
}

测试代码

public class Demo {public static void main(String[] args) throws InterruptedException {MyThreadPool myThreadPool = new MyThreadPool();for (int i = 0; i < 5; i++) {final int taskId = i;myThreadPool.submit(() -> someTask(Thread.currentThread().getName(),taskId));}Thread.sleep(1000);}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(2000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}}

运行结果:
在这里插入图片描述


  各位看官如果觉得文章写得不错,点赞评论关注走一波!谢谢啦!。

在这里插入图片描述

相关文章:

《Java-SE-第二十六章》之线程池

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水平很有限&…...

【数据库】将excel数据导入mysql数据库

环境&#xff1a;Windows10 mysql8以上 将你要导入的excel表另存为txt格式 打开txt格式文件&#xff0c;删除表头行并另存为并更改编码方式&#xff08;由于与数据库的编码不同&#xff0c;会导致导入报错&#xff09; 通过命令行登录数据库 winr cmd进入 进入装mysql的目录位…...

无涯教程-Lua - repeat...until 语句函数

与 for 和 while 循环(它们在循环顶部测试循环条件)不同&#xff0c;Lua编程中的 repeat ... until 循环语言在循环的底部检查其条件。 repeat ... until 循环与while循环相似&#xff0c;不同之处在于&#xff0c;保证do ... while循环至少执行一次。 repeat...until loop - …...

环形链表 LeetCode热题100

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 思路 快慢指针。开始快指针在慢指针前面&#xff0c;当快指针等于慢指针时说明有环&#xff0c;如果快指针指向null时说明无环。 代码 /*** Definition for singly-linked list.* struct ListNode {* …...

使用python将每组两行数据合并一行

1、使用场景 将有规律的每组(一组2行)的单数行和双数行合并为一行&#xff0c;以空格分割。 比如使用pssh批量得出的结果&#xff0c;想让ip行和结果行合并为一行&#xff08;前提如上所述&#xff09; [rootk8s-master1 tmp]# pssh -h iplist -i hostname [1] 18:12:42 [SU…...

14-1_Qt 5.9 C++开发指南_网络编程及主机信息查询_HostInfo

Qt 网络模块提供了用于编写 TCP/IP 客户端和服务器端程序的各种类&#xff0c;如用于 TCP 通信的QTcpSocket 和 QTcpServer&#xff0c;用于 UDP 通信的 QUdpSocket&#xff0c;还有用于实现 HTTP、FTP 等普通网络协议的高级类如 QNetworkRequest&#xff0c;QNetworkReply 和Q…...

【iOS】通知原理

我们可以通过看通知的实现机制来了解通知中心是怎么实现对观察者的引用的。由于苹果对Foundation源码是不开源的&#xff0c;我们具体就参考一下GNUStep的源码实现。GNUStep的源码地址为&#xff1a;GNUStep源码GitHub下载地址, 具体源码可以进行查看。 通知的主要流程 通知全…...

创建邮件服务器(小微企业)

这里写自定义目录标题 目的硬件选型&#xff1a;软件选型&#xff1a;coremail &#xff08;商业版本&#xff09;postfixumail免费开源版本新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适…...

android app控制ros机器人四(调整界面布局)

半吊子改安卓&#xff0c;记录页面布局调整&#xff1a; 在ros-mobile基础上顶端增加一行&#xff0c;用于显示app名称和logo图像&#xff1b;修改标签页。 添加文字简单&#xff0c;但是替换图标长知识了&#xff0c;开始只是简单的把mipmap各个文件夹下的图片进行替换&…...

稍微深度踩坑haystack + whoosh + jieba

说到django的全文检索&#xff0c;网上基本推荐的都是 haystack whoosh jieba 的方案。 由于我的需求对搜索时间敏感度较低&#xff0c;但是要求不能有数据的错漏。 但是没有调试的情况下&#xff0c;搜索质量真的很差&#xff0c;搞得我都想直接用Like搜索数据库算了。 但是…...

微信小程序(van-tabs) 去除横向滚动条样式(附加源码解决方案+报错图)

问题描述 今天第一次接触vant组件库。 ant官网地址适用于Vue3 支持Vue2、Vue3、微信小程序等 我在使用van-tabs组件时遇到了一个问题&#xff0c;如下图所示&#xff1a; 从图片上可以看到有个灰色的横向滚动条&#xff0c;一开始领导给我说这个问题&#xff0c;我反反复复都…...

激光切割机所发出的辐射是否会对人体产生危害呢

激光切割设备所发出的激光作为一种特殊的能量光源&#xff0c;在一定程度上是存在辐射的。由于光纤激光器的功率通常大于半导体激光器&#xff0c;因此其辐射安全性也受到我们的关注。那么这种辐射的危害究竟有多大呢? 第一级&#xff1a;在正常操作下&#xff0c;不会发出对人…...

Redis 高可用:主从复制、哨兵模式、集群模式

文章目录 一、redis高可用性概述二、主从复制2.1 主从复制2.2 数据同步的方式2.2.1 全量数据同步2.2.2 增量数据同步 2.3 实现原理2.3.1 服务器 RUN ID2.3.2 复制偏移量 offset2.3.3 环形缓冲区 三、哨兵模式3.1 原理3.2 配置3.3 流程3.4 使用3.5 缺点 四、cluster集群4.1 原理…...

在GitHub上管理和协作的完全指南

介绍 GitHub 是一个强大的版本控制和协作平台&#xff0c;它不仅可以帮助你管理和跟踪项目的变化&#xff0c;还可以与他人进行协作。本文将详细介绍如何使用 GitHub 的各种功能来管理和协作项目。 目录 注册GitHub账号创建和管理仓库 创建仓库添加和管理文件分支管理合并请…...

git管理工具学习(图解使用git工作流程)

目录 GIT 简介一个最简单的GIT操作流程git的工作流程&命令 GIT 简介 git是什么&#xff0c;在维基百科上是这么介绍的&#xff1a;git是一个分布式的版本控制软件 分布式是相对于集中式而言的&#xff0c;分布式即每一个git库都是一个完整的库。 每个库的地位都是平等的&am…...

单例模式(Singleton)

单例模式保证一个类仅有一个实例&#xff0c;并提供一个全局访问点来访问它&#xff0c;这个类称为单例类。可见&#xff0c;在实现单例模式时&#xff0c;除了保证一个类只能创建一个实例外&#xff0c;还需提供一个全局访问点。 Singleton is a creational design pattern t…...

2023-08-02 LeetCode每日一题(翻转卡片游戏)

2023-08-02每日一题 一、题目编号 822. 翻转卡片游戏二、题目链接 点击跳转到题目位置 三、题目描述 在桌子上有 N 张卡片&#xff0c;每张卡片的正面和背面都写着一个正数&#xff08;正面与背面上的数有可能不一样&#xff09;。 我们可以先翻转任意张卡片&#xff0c;…...

JAVAWEB项目--POST完整交互(servlet,axios,JavaScript)

post交互 js&#xff1a; axios.post("/mycsdn/blog/pageSer", {currentPage:currentPage,}).then(function (response) {window.location.href url;}).catch(function (error) {console.error("分页未遂", error);}); 后端servlet&#xff1a; public…...

统一观测|借助 Prometheus 监控 ClickHouse 数据库

引言 ClickHouse 作为用于联机分析(OLAP)的列式数据库管理系统(DBMS), 最核心的特点是极致压缩率和极速查询性能。同时&#xff0c;ClickHouse 支持 SQL 查询&#xff0c;在基于大宽表的聚合分析查询场景下展现出优异的性能。因此&#xff0c;获得了广泛的应用。本文旨在分享阿…...

【Golang】基于录制,自动生成go test接口自动化用例

目录 背景 框架 ginkgo初始化 抓包&运行脚本 目录说明 ∮./business ∮./conf ∮./utils ∮./testcase testcase 用例目录结构规则 示例 实现思路 解析Har数据 定义结构体 解析到json 转换请求数据 转换请求 转换请求参数 写业务请求数据 写gotest测试…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准

城市路内停车管理常因行道树遮挡、高位设备盲区等问题&#xff0c;导致车牌识别率低、逃费率高&#xff0c;传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法&#xff0c;正成为破局关键。该设备安装于车位侧方0.5-0.7米高度&#xff0c;直接规避树枝遮…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...

FFmpeg avformat_open_input函数分析

函数内部的总体流程如下&#xff1a; avformat_open_input 精简后的代码如下&#xff1a; int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

yaml读取写入常见错误 (‘cannot represent an object‘, 117)

错误一&#xff1a;yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因&#xff0c;后面把yaml.safe_dump直接替换成yaml.dump&#xff0c;确实能保存&#xff0c;但出现乱码&#xff1a; 放弃yaml.dump&#xff0c;又切…...

41道Django高频题整理(附答案背诵版)

解释一下 Django 和 Tornado 的关系&#xff1f; Django和Tornado都是Python的web框架&#xff0c;但它们的设计哲学和应用场景有所不同。 Django是一个高级的Python Web框架&#xff0c;鼓励快速开发和干净、实用的设计。它遵循MVC设计&#xff0c;并强调代码复用。Django有…...

高抗扰度汽车光耦合器的特性

晶台光电推出的125℃光耦合器系列产品&#xff08;包括KL357NU、KL3H7U和KL817U&#xff09;&#xff0c;专为高温环境下的汽车应用设计&#xff0c;具备以下核心优势和技术特点&#xff1a; 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计&#xff0c;确保在…...

工厂方法模式和抽象工厂方法模式的battle

1.案例直接上手 在这个案例里面&#xff0c;我们会实现这个普通的工厂方法&#xff0c;并且对比这个普通工厂方法和我们直接创建对象的差别在哪里&#xff0c;为什么需要一个工厂&#xff1a; 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类&#xff1a; 两个发…...