JAVA线程池原理详解二
JAVA线程池原理详解二
一. Executor框架
Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。
Executor的UML图:(常用的几个接口和子类)
Executor:一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command)。
ExecutorService:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法。
AbstractExecutorService:ExecutorService执行方法的默认实现。
ScheduledExecutorService:一个可定时调度任务的接口。
ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池。
ThreadPoolExecutor:线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象。
在前面介绍的JAVA线程既是工作单元,也是执行机制。而在Executor框架中,我们将工作单元与执行机制分离开来。Runnable和Callable是工作单元(也就是俗称的任务),而执行机制由Executor来提供。这样一来Executor是基于生产者消费者模式的,提交任务的操作相当于生成者,执行任务的线程相当于消费者。
1、从类图上看,Executor接口是异步任务执行框架的基础,该框架能够支持多种不同类型的任务执行策略。
public interface Executor {void execute(Runnable command);
}
Executor接口就提供了一个执行方法,任务是Runnbale类型,不支持Callable类型。
2、ExecutorService接口实现了Executor接口,主要提供了关闭线程池和submit方法:
public interface ExecutorService extends Executor {List<Runnable> shutdownNow();boolean isTerminated();<T> Future<T> submit(Callable<T> task);}
另外该接口有两个重要的实现类:ThreadPoolExecutor与ScheduledThreadPoolExecutor。
其中ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务;而ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行任务,或者定期执行命令。
在上一篇文章中,我是使用ThreadPoolExecutor来通过给定不同的参数从而创建自己所需的线程池,但是在后面的工作中不建议这种方式,推荐使用Exectuors工厂方法来创建线程池
这里先来区别线程池和线程组(ThreadGroup与ThreadPoolExecutor)这两个概念:
a、线程组就表示一个线程的集合。
b、线程池是为线程的生命周期开销问题和资源不足问题提供解决方案,主要是用来管理线程。
Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadExecutor和CachedThreadPool
a、SingleThreadExecutor:单线程线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}
我们从源码来看可以知道,单线程线程池的创建也是通过ThreadPoolExecutor,里面的核心线程数和线程数都是1,并且工作队列使用的是无界队列。由于是单线程工作,每次只能处理一个任务,所以后面所有的任务都被阻塞在工作队列中,只能一个个任务执行。
b、FixedThreadExecutor:固定大小线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}
这个与单线程类似,只是创建了固定大小的线程数量。
c、CachedThreadPool:无界线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}
无界线程池意味着没有工作队列,任务进来就执行,线程数量不够就创建,与前面两个的区别是:空闲的线程会被回收掉,空闲的时间是60s。这个适用于执行很多短期异步的小程序或者负载较轻的服务器。
Callable、Future、FutureTash详解
Callable与Future是在JAVA的后续版本中引入进来的,Callable类似于Runnable接口,实现Callable接口的类与实现Runnable的类都是可以被线程执行的任务。
三者之间的关系:
Callable是Runnable封装的异步运算任务。
Future用来保存Callable异步运算的结果
FutureTask封装Future的实体类
1、Callable与Runnbale的区别
a、Callable定义的方法是call,而Runnable定义的方法是run。
b、call方法有返回值,而run方法是没有返回值的。
c、call方法可以抛出异常,而run方法不能抛出异常。
2、Future
Future表示异步计算的结果,提供了以下方法,主要是判断任务是否完成、中断任务、获取任务执行结果
public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
3、FutureTask<V>
可取消的异步计算,此类提供了对Future的基本实现,仅在计算完成时才能获取结果,如果计算尚未完成,则阻塞get方法。
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
FutureTask不仅实现了Future接口,还实现了Runnable接口,所以不仅可以将FutureTask当成一个任务交给Executor来执行,还可以通过Thread来创建一个线程。
Callable与FutureTask
定义一个callable的任务:
public class MyCallableTask implements Callable<Integer>{@Overridepublic Integer call() throws Exception{System.out.println("callable do something");Thread.sleep(100);return new Random().nextInt(100);}}
public class CallableTest
{public static void main(String[] args) throws Exception{Callable<Integer> callable = new MyCallableTask();FutureTask<Integer> future = new FutureTask<Integer>(callable);Thread thread = new Thread(future);thread.start();Thread.sleep(100);//尝试取消对此任务的执行future.cancel(true);//判断是否在任务正常完成前取消System.out.println("future is cancel:" + future.isCancelled());if(!future.isCancelled()){System.out.println("future is cancelled");}//判断任务是否已完成System.out.println("future is done:" + future.isDone());if(!future.isDone()){System.out.println("future get=" + future.get());}else{//任务已完成System.out.println("task is done");}}
}
执行结果:
callable do somothing
future is cancel:true
future is done:true
task is done
这个DEMO主要是通过调用FutureTask的状态设置的方法,演示了状态的变迁。
a、第11行,尝试取消对任务的执行,该方法如果由于任务已完成、已取消则返回false,如果能够取消还未完成的任务,则返回true,该DEMO中由于任务还在休眠状态,所以可以取消成功。
future.cancel(true);
b、第13行,判断任务取消是否成功:如果在任务正常完成前将其取消,则返回true
System.out.println("future is cancel:" + future.isCancelled());
c、第19行,判断任务是否完成:如果任务完成,则返回true,以下几种情况都属于任务完成:正常终止、异常或者取消而完成。 我们的DEMO中,任务是由于取消而导致完成。
System.out.println("future get=" + future.get());
d、在第22行,获取异步线程执行的结果,我这个DEMO中没有执行到这里,需要注意的是,future.get方法会阻塞当前线程, 直到任务执行完成返回结果为止。
System.out.println("future get=" + future.get());
Callable与Future
public class CallableThread implements Callable<String>private SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
{@Overridepublic String call()throws Exception{System.out.println("进入Call开始休眠。休眠时间:" + df.format(new Date(System.currentTimeMillis())));Thread.sleep(10000);return "睡觉啦";}public static void main(String[] args) throws Exception{Callable<String> callable = new CallableThread();ExecutorService executor = Executors.newSingleThreadExecutor();Future<String> submit = executor.submit(callable);executor.shutdown();Thread.sleep(5000);System.out.println("主线程休眠5秒。当前时间:" + df.format(new Date(System.currentTimeMillis())) );String s = submit.get();System.out.println("Future数据已拿到,str=" + s + ",当前时间:" + df.format(new Date(System.currentTimeMillis())));}
}
执行结果:
进入Call开始休眠。休眠时间:2023-02-28 17:09:17:629
主线程休眠5秒。当前时间:2023-02-28 17:09:22:631
Future数据已拿到,str=睡觉啦,当前时间:2023-02-28 17:09:27:645
这里的future是直接扔到线程池里面去执行的。由于要打印任务的执行结果,所以从执行结果来看,主线程虽然休眠了5s,但是从Call方法执行到拿到任务的结果,这中间的时间差正好是10s,说明get方法会阻塞当前线程直到任务完成。
通过FutureTask也可以达到同样的效果:
public static void main(String[] args) throws Exception{ExecutorService es = Executors.newSingleThreadExecutor();Callable<String> call = new CallableThread();FutureTask<String> task = new FutureTask<String>(call);es.submit(task);es.shutdown();Thread.sleep(5000);System.out.println("主线程休眠5秒。当前时间:" + df.format(new Date(System.currentTimeMillis())) );String s = submit.get();System.out.println("Future数据已拿到,str=" + s + ",当前时间:" + df.format(new Date(System.currentTimeMillis())));}
以上的组合可以给我们带来这样的一些变化:
如有一种场景中,方法A返回一个数据需要10s,A方法后面的代码运行需要20s,但是这20s的执行过程中,只有后面10s依赖于方法A执行的结果。如果与以往一样采用同步的方式,势必会有10s的时间被浪费,如果采用前面两种组合,则效率会提高:
1、先把A方法的内容放到Callable实现类的call()方法中
2、在主线程中通过线程池执行A任务
3、执行后面方法中10秒不依赖方法A运行结果的代码
4、获取方法A的运行结果,执行后面方法中10秒依赖方法A运行结果的代码
这样代码执行效率一下子就提高了,程序不必卡在A方法处。
相关文章:

JAVA线程池原理详解二
JAVA线程池原理详解二 一. Executor框架 Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生…...

Java 常用 API
文章目录一、Math二、System三、Object1. toString() 方法2. equals() 方法四、Arrays1. 冒泡排序2. Arrays 常用方法五、基本类型包装类1. Integer2. int 和 String 相互转换3. 字符串中数据排序4. 自动装箱和拆箱六、日期类1. Date2. SimpleDateFormat3. Calendar4. 二月天一…...
记一次分布式环境下TOKEN实现用户登录
背景: 以前的单体项目,使用的是session来保存用户登录状态,控制用户的登录过期时间等信息,但是这个session是只保存在该服务器的这个系统内存中。系统只有一个服务就没关系,但是如果是分布式的服务,每个…...

用cpolar发布本地的论坛网站 1
网页论坛向来是个很神奇的地方,曾经的天涯论坛和各种BBS,大家聚在在一起讨论某个问题,也能通过论坛发布想法,各种思维碰撞在一起,发生很多有趣的故事,也产生了很多流传一时的流行语录。当然,如果…...
CSS的4种引入方式
CSS的4种引入方式 目录CSS的4种引入方式一、内嵌式:CSS写在style标签中二、外联式:CSS写在一个单独的.css文件中三、行内式:CSS写在标签的style属性中四、导入外部样式五、css引用的优先级六、link和import的区别一、内嵌式:CSS写…...

Shell高级——Linux中的文件描述符(本质是数组的下标)
以下内容源于C语言中文网的学习与整理,非原创,如有侵权请告知删除。 前言 Linux中一切接文件,比如 C 源文件、视频文件、Shell脚本、可执行文件等,就连键盘、显示器、鼠标等硬件设备也都是文件。 一个 Linux 进程可以打开成百上…...

Nvidia jetson nano硬件架构
资料来源 官方文档中心 https://developer.nvidia.com/embedded/downloads -> 选jetson -> Jetson Nano Product Design Guide //产品设计指导(入口) //-> 1.1 References 列出了相关的文档 -> Jetson Nano Developer Kit Carrier Board Specification //板子标注…...

ffmpeg多路同时推流
一、ffmpeg常见使用方法1.1利用FFMPEG命令进行文件分割1.2转换格式1.3推流配置方法一:ngnix(不推荐,推流不好使)方法二:srs(强烈推荐)1.4查看nginx启动是否成功二、ffmpeg推流——>ngnix单路…...

一次性搞定 `SHOW SLAVE STATUS` 的解读
一次性搞定 SHOW SLAVE STATUS 的解读 解析日志文件的位置 诚然, GTID(全局事务标识符)已经在 MySQL 5.6中得到支持, 此外,还可以通过 Tungsten replicator 软件来实现(2009年以后一直有谷歌在维护,不是吗?)。 但有一部分人还在使用MySQL 5.5的标准副本方式, 那么这些二进制日…...
【代码随想录训练营】【Day25】第七章|回溯算法 |216.组合总和III|17.电话号码的字母组合
组合总和III 题目详细:LeetCode.216 做过上一题组合后,再来写这道题就显得得心应手了,通过理解回溯算法的模版,也总结出了算法中的一些特点: 回溯算法与递归算法类似,同样需要参数、结束条件和主体逻辑回…...
docker使用
https://blog.csdn.net/u012563853/article/details/125295985http://www.ppmy.cn/news/11249.html启动 docker服务并设置开机自动启动dockersudo systemctl start docker sudo systemctl enable dockerdocker 常见启动失败问题:https://blog.csdn.net/zhulianseu/article/deta…...
手把手docker registry配置登录名/密码
我们的Docker私有仓库Registry服务只有加了认证机制之后我们的Registry服务才会更加的安全可靠。赶快跟随以下步骤来增加认证机制吧。 创建docker registry工作目录 mkdir -p /data/docker.registry 创建将保存凭据的文件夹 mkdir -p /data/docker.registry/etc/registry/auth…...

一步打通多渠道服务场景 中电金信源启移动开发平台MADP功能“上新”
日前,中电金信源启移动开发平台MADP功能迭代升级,“上新”源启小程序开发平台。定位“为金融业定制”的移动PaaS平台,源启小程序开发平台为银行、互联网金融、保险、证券客户提供一站式小程序的开发、运营、营销全生命周期管理技术支撑&#…...

Kubernetes06:Controller (Deployment无状态应用)
Kubernetes06:Controller 1、什么是controller 管理和运行容器的对象,是一个物理概念 在集群上管理和运行容器的对象 2、Pod和Controller之间的关系 Pod是通过controller来实现应用的运维 比如伸缩、滚动升级等等操作Pod和Controller之间通过 label 标签建立关系…...

低代码开发平台选型必看指南
低代码开发是近年来逐渐兴起的一种新型软件开发方式。它通过封装常见的软件开发流程和代码,使得非专业的开发者也能够轻松创建复杂的应用程序。这种开发方式已经受到了许多企业的青睐,成为提高生产效率、降低开发成本的一种有效途径。 低代码开发的核心…...

OVN:ovn20.03.1/ovs2.13.0编译rpm过程
操作系统openeuler22.0,x86架构分别下载ovn和ovs的源码https://github.com/openvswitch/ovs/tree/v2.13.0https://github.com/ovn-org/ovn/tree/v20.03.1安装必要工具:yum install -y unzip tar make autoconf automake libtool rpm-build gcc libuuid-d…...
Shell管道
一、管道是什么 英文是pipe。 把一个命令的标准输出作为下一个命令的标准输入,以这种方式连接的两个或者多个命令就形成了管道 使用竖线|连接多个命令,称为管道符。 语法格式如下: command1 | command2 [ | commandN... ] command1的标准…...

Zynq UltraScale系列使用MIPI CSI-2 RX Subsystem 解码MIPI视频PD输出 提供2套工程源码和技术支持
目录1、前言2、设计思路和架构3、vivado工程详解4、上板调试验证5、福利:工程代码的获取1、前言 本设计采用OV5640摄像头MIPI模式作为输入,分辨率为1280x72060Hz,MIPI解码方案采用Xilinx官方提供的MIPI CSI-2 RX Subsystem IP解码MIPI视频&a…...
C++:详解C++11 线程休眠函数
休眠函数简介1: 让线程休眠一段时间1.1:std::chrono 的时钟 clock简介 C11 之前并未提供专门的休眠函数,C语言的 sleep、usleep函数其实是系统提供的函数,不同的系统函数的功能还要些差异。 在Windows系统中,sleep的参数是毫秒 …...

TryHackMe-The Great Escape(Docker)
The Great Escape 我们的开发人员创建了一个很棒的新网站。你能冲出沙盒吗? 端口扫描 循例 nmap Web信息收集 robots.txt: /exif-util是文件上传点,但是绕过之后貌似没啥用 在robots.txt当中披露了可能存在.bak.txt,现在我们已知的文件就是…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...
前端工具库lodash与lodash-es区别详解
lodash 和 lodash-es 是同一工具库的两个不同版本,核心功能完全一致,主要区别在于模块化格式和优化方式,适合不同的开发环境。以下是详细对比: 1. 模块化格式 lodash 使用 CommonJS 模块格式(require/module.exports&a…...