【Android】线程池的解析

引言

在Android当中根据用途分为主线程与子线程,主线程当中主要处理与界面相关的操作,子线程主要进行耗时操作。除了Thread本身以外,在Android当中还有很多扮演者线程的角色,比如AsyncTask( 底层为线程池,但是现在并不推荐使用)、IntentService和一个特殊的线程HandlerThread。

对于不同的线程有不同的使用场景,AsyncTask封装了线程池和Handler,主要是为了在子线程里面更新UI。HandlerThread是一种具有消息循环的线程,它的内部可以使用Handler。IntentService是一个服务,系统对内部进行了封装使其更方便的进行后台服务,内部采用HandlerThread来执行任务,当任务执行完毕IntentService会自动退出,它的作用很像一个后台进程(被弃用,WorkManagerJobIntentServiceWorkManager 是 Google 推荐的用于执行后台任务的解决方案,它支持一次性任务和周期性任务,并能够处理任务的重试、链式依赖等。而 JobIntentService 可以在后台处理任务,并且在需要时重新启动服务,适用于需要向后兼容较旧的 Android 版本的场景)。

在操作系统当中,线程是操作系统调度的最小单元, 同时线程又是一种受限的系统资源,即线程不可以无限制的产生,并且线程的创建和销毁都会有相应的开销。当系统当中存在大量的线程的时候,系统会通过时间片轮转的方式调度线程,因此线程不可能做到绝对的并行,除非线程数量小于CPU的核心数,但一般来说这是不可能的。但是在程序当中频繁创建和销毁线程显然不是高效的做法,应该采用线程池,接下来就看看Android中的线程池吧!

使用线程池的优点

  1. 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
  2. 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
  3. 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。

ThreadPoolExecutor

ThreadPoolExecutor 是 Java 中 Executor 框架的一部分,它实现了 Executor 接口和 ExecutorService 接口。这个类允许你创建一个线程池,并且可以控制任务的并发执行,它是线程池的核心实现类。

一共有4个构造方法,接下来我们就看看拥有最多参数的构造方法

在这里插入图片描述

  1. corePoolSize(核心线程数)

默认情况下线程池是空的,只有提交任务时才会创建线程。如果当前运行的线程数少于corePoolSize,则会创建新的线程来处理任务;如何当前运行的线程数等于或者多于corePoolSize,则不会创建新线程。核心线程通常不会被回收(除非设置了允许回收的核心线程数)。如果调用线程池的prestartAllcoreThread方法,则线程池会提前创建并开启所有的核心线程来处理任务。

  1. maximumPoolSize(最大线程数)

这是线程池中允许的最大线程数量,包括核心线程和非核心线程。当队列满了并且正在执行的线程数少于最大线程数时,线程池会尝试创建新的线程来处理任务。如果队列满了且线程数已达到最大线程数,新提交的任务将被拒绝。

  1. keepAliveTime(非核心线程空闲存活时间)

这是非核心线程在终止前等待新任务的最长时间。当线程池中的线程数超过核心线程数时,这些额外的线程(非核心线程)在空闲时会等待新任务的到来。如果超过这个时间还没有新任务,线程将被回收。对于核心线程,这个参数无效,除非设置了允许回收的核心线程数(allowCoreThreadTimeOut(true)方法来设置)。

  1. unit(时间单位)

这是keepAliveTime参数的时间单位,可以是毫秒、秒、分钟等。

  1. workQueue(工作队列)

这是一个阻塞队列,用于存放待执行的任务。当所有核心线程都在忙碌时,新提交的任务会被放入这个队列中。如果队列满了,线程池会尝试创建新的线程来处理任务,直到达到最大线程数。

  1. threadFactory(线程工厂)

这是一个ThreadFactory对象,用于创建新线程。线程工厂允许你自定义线程的创建过程,例如设置线程的名称、优先级、守护状态等。默认的线程工厂通常就足够了,但自定义线程工厂可以提供更多的控制和调试信息。

  1. handler(拒绝/饱和策略)

这是一个RejectedExecutionHandler对象,用于处理当任务太多,无法被线程池及时处理时的情况,即任务队列和线程池都满了的情况。常见的拒绝策略有:

  • AbortPolicy:默认策略,表示无法处理新任务,抛出RejectedExecutionException
  • CallerRunsPolicy:在调用者的线程中执行任务。
  • DiscardPolicy:默默丢弃无法处理的任务。
  • DiscardOldestPolicy:丢弃队列中最旧的任务,重新提交当前的新任务。

线程池的处理流程与原理

在这里插入图片描述

根据流程图我们可以看到,当我们执行ThreadPoolExecutor的execute方法,会有各种的情况:

  • 如果线程池中的线程数未达到核心线程数,则创建核心线程处理任务
  • 如果线程数大于或等于核心线程数,则将任务加入任务队列,线程池中的空闲线程会不断地从任务队列中取出任务进行处理
  • 如果任务队列满了,并且线程数没有达到最大线程数,则创建非核心线程去处理任务
  • 如果线程数超过了最大线程数,则执行饱和策略

线程池的种类

我们可以直接或者间接的通过配置来实现自己的线程池的功能特性。

FixedThreadPool

先来看看它的构造函数:

在这里插入图片描述

public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
  • nThreads:核心线程数和最大线程数都被设置为nThreads,这意味着线程池的大小是固定的,不会动态变化。
  • 0L:非核心线程的空闲存活时间被设置为0。由于所有线程都是核心线程,这个值实际上并不会影响线程池的行为。
  • TimeUnit.MILLISECONDS:空闲存活时间的时间单位是毫秒。
  • new LinkedBlockingQueue<Runnable>():工作队列是一个无界的LinkedBlockingQueue。由于线程池的大小是固定的,这个无界队列意味着如果所有线程都在忙碌,新提交的任务将会被放入队列中,直到队列满为止。

是可重用固定线程数的线程池,在一开始创建就已经规定了线程数,意味着只有核心线程没有非核心线程,即创建的都是核心线程,并且这些线程会一直存活直到线程池被关闭,即使它们处于空闲状态也不会被回收。它的提交任务执行示意图:

在这里插入图片描述

CachedThreadPool

在这里插入图片描述

public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
  • 0:核心线程数被设置为0,这意味着线程池在初始时没有任何线程,线程池中的线程都是非核心线程。
  • Integer.MAX_VALUE:最大线程数被设置为Integer.MAX_VALUE(约21亿),这意味着线程池理论上可以创建非常多的线程。但由于实际物理和操作系统资源的限制,这个数字通常不会达到。
  • 60L:非核心线程的空闲存活时间被设置为60秒。当线程池中的线程空闲超过这个时间,它们将被回收。

CachedThreadPool线程池,它会根据需要创建新线程,但如果线程空闲超过一定时间(默认60秒),则会被回收。这种线程池适合执行很多短期异步任务的程序。

在这里插入图片描述

SingleThreadExecutor

SingleThreadExecutor是使用单个线程的线程池,当当前没有运行的线程的时候,就会创建一个新线程来处理任务,如果有运行的线程就将其添加到阻塞队列当中。

在这里插入图片描述

public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
  • 1:核心线程数和最大线程数都被设置为1,这意味着线程池始终只有一个线程。
  • 0L:非核心线程的空闲存活时间被设置为0。由于只有一个线程,这个值实际上并不会影响线程池的行为。

在这里插入图片描述

ScheduledThreadPool

ScheduledThreadPool是一个能实现和定时和周期性任务的线程池。

在这里插入图片描述

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);
}public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,new DelayedWorkQueue());
}

new DelayedWorkQueue():工作队列,这里使用了一个延迟工作队列。这个队列可以存储待执行的任务,并按照任务的延迟时间进行排序,确保最早需要执行的任务可以被优先处理

在这里插入图片描述

当执行ScheduledThreadPoolExecutorscheduleAtFixedRate 或者scheduleWithFixedDelay 方法时,会向 DelayedWorkQueue 添加一个实现 RunnableScheduledFuture 接口的 ScheduledFutureTask(任务的包装类),并会检查运行的线程数是否达到了corePoolSize(核心线程数)。如果没有达到,则新建线程并启动它,但并不是立即去执行任务,而是去DelayedWorkQueue中取ScheduledFutureTask,然后执行任务。如果运行的线程数达到了corePoolSize时,则将任务添加到 DelayedWorkQueue 中。DelayedWorkQueue 会将任务进行排序,先要执行的任务放在队列的前面。其跟此前介绍的线程池不同的是,当执行完任务后,会将ScheduledFutureTask中的 time变量改为下次要执行的时间并放回 DelayedWorkQueue中。

总结

线程池类型特点适用场景核心线程数最大线程数空闲线程存活时间
FixedThreadPool拥有固定数量的线程,线程数不变。负载较重的服务器,需要限制线程数量的场景。固定固定
CachedThreadPool根据需要创建新线程,空闲线程会被回收。执行很多短期异步任务的程序。0Integer.MAX_VALUE60秒
ScheduledThreadPool可以安排在给定延迟后运行命令或定期地执行。需要任务在后台定期执行或重复执行的程序。固定固定60秒
SingleThreadExecutor只有一个线程,所有任务按照提交顺序依次执行。需要保证任务顺序执行的场景。11

文章到这里就结束了!

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

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

相关文章

【Linux网络编程】简单的UDP套接字

目录 一&#xff0c;socket编程的相关说明 1-1&#xff0c;sockaddr结构体 1-2&#xff0c;Socket API 二&#xff0c;基于Udp协议的简单通信 三&#xff0c;UDP套接字的应用 3-1&#xff0c;实现英译汉字典 一&#xff0c;socket编程的相关说明 Socket编程是一种网络通信…

【java基础】微服务篇

参考黑马八股视频。 目录 Spring Cloud 5大组件 注册中心 负载均衡 限流 CAP和BASE 分布式事务解决方案 分布式服务的接口幂等性 分布式任务调度 Spring Cloud 5大组件 注册中心 Eureka的作用 健康监控 负载均衡 限流 漏桶固定速率&#xff0c;令牌桶不限速 CAP和BA…

java八股-SpringCloud微服务-Eureka理论

文章目录 SpringCloud架构Eureka流程Nacos和Eureka的区别是&#xff1f;CAP定理Ribbon负载均衡策略自定义负载均衡策略如何实现&#xff1f;本章小结 SpringCloud架构 Eureka流程 服务提供者向Eureka注册服务信息服务消费者向注册中心拉取服务信息服务消费者使用负载均衡算法挑…

thinkphp6安装php-mqtt/client,并实现实时消息收发写入日志

thinkphp6安装php-mqtt/client,并实现实时消息收发写入日志 系统&#xff1a;centos7 第一步&#xff1a;宝塔面板安装php环境8.0&#xff1b; 第二步&#xff1a;宝塔自带安装composer; 第三步&#xff1a;下载thinkphp6 create project composer require topthink/think…

.NET9 - 新功能体验(一)

被微软形容为“迄今为止最高效、最现代、最安全、最智能、性能最高的.NET版本”——.NET 9已经发布有一周了&#xff0c;今天想和大家一起体验一下新功能。 此次.NET 9在性能、安全性和功能等方面进行了大量改进&#xff0c;包含了数千项的修改&#xff0c;今天主要和大家一起体…

K8S资源限制之ResourceQuota

ResourceQuota介绍 在K8S中&#xff0c;大部分资源都可以指定到一个名称空间下&#xff0c;因此可以对一个名称空间的计算资源&#xff0c;存储资源&#xff0c;资源数量等维度做资源限制。 如限制pod数量、svc数量&#xff0c;控制器数量&#xff0c;限制PVC请求的存储量 注…

永磁同步电机无速度算法--滑模观测器(PLL)

一、原理介绍 在永磁同步电机滑模观测器控制中&#xff0c;转子的位置和转速信息与反动电势密切相关。滑模观测器控制基本设计思路是&#xff1a;利用永磁同步电机的电压、电流信息&#xff0c;通过永磁同步电机数学模型&#xff0c;估算出电机在两相静止坐标系中的反电动势信…

NIO 与传统 IO:深入理解与应用场景

在 Java 编程中&#xff0c;IO&#xff08;输入/输出&#xff09;操作是不可或缺的一部分。Java 提供了两种主要的 IO 机制&#xff1a;传统的阻塞式 IO&#xff08;Blocking IO&#xff09;和非阻塞式 IO&#xff08;Non-blocking IO&#xff09;&#xff0c;后者通常被称为 N…

TensorFlow 2.0 windows11 GPU 训练环境配置

前言 在一切开始之前&#xff0c;请确保你的cmd命令行和powershell命令行可以正常打开。如果不能&#xff0c;建议重装系统。我不确定这是否会影响你最终的结果&#xff0c;毕竟windows的坑太多了。 安装顺序&#xff1a;visual studio -> cuda -> cudnn -> python…

使⽤MATLAB进⾏⽬标检测

目录 数据准备定义模型并训练用测试集评估性能推理过程⼀⾏代码查看⽹络结构⼀⾏代码转onnx结语 ⼈⽣苦短&#xff0c;我⽤MATLAB。 Pytorch在深度学习领域占据了半壁江⼭&#xff0c;最主要的原因是⽣态完善&#xff0c;⽽且api直观易⽤。但谁能想到现在MATLAB⽤起来⽐Pytorch…

【Java】实战:多数元素

一、题目描述 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#x…

JAVA:探索 PDF 文字提取的技术指南

1、简述 随着信息化的发展&#xff0c;PDF 文档成为了信息传播的重要媒介。在许多应用场景下&#xff0c;如数据迁移、内容分析和信息检索&#xff0c;我们需要从 PDF 文件中提取文字内容。JAVA提供了多种库来处理 PDF 文件&#xff0c;其中 PDFBox 和 iText 是最常用的两个。…

vue3+vant实现弹幕循环播放~

1、效果图 <!-- 弹幕 --> <div style"height: 88px"><van-barragev-model"list"duration"5000":rows"rows":gap"gap":loop"loop"style"--move-distance: -345px" ><div class&quo…

南京邮电大学算法设计-二叉树先序遍历算法动态演示

二叉树先序遍历算法动态演示 一、课题内容和要求 (1)实验目的&#xff1a; 本实验通过手动输入二叉树结点信息&#xff0c;构建相应的二叉树&#xff0c;并通过图形化界面动态演示先序遍历算法的过程。通过本次实验&#xff0c;我可以深入理解二叉树的数据结构、先序遍历算法…

大数据挖掘期末复习

大数据挖掘 数据挖掘 数据挖掘定义 技术层面&#xff1a; 数据挖掘就是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中&#xff0c;提取隐含在其中、人们事先不知道的、但又潜在有用的信息的过程。 数据准备环节 数据选择 质量分析 数据预处理 数据仓库 …

【Anomaly Detection论文阅读记录】Resnet网络与WideResNet网络

Resnet网络 网络结构&#xff1a;(层数计算不包括max pool、average pool、softmax等操作) 层数计算&#xff08;以Resnet-18为例子&#xff09;&#xff1a; conv1conv2_xconv3_xconv4_xconv5_xfc1(22)(22)(22)(22)118 WideResNet网络 WideResNet提出了一种新的体系结构&#…

基于YOLOv8深度学习的汽车车身车损检测系统研究与实现(PyQt5界面+数据集+训练代码)

本文研究并实现了一种基于YOLOV8深度学习模型的汽车车身车损检测系统&#xff0c;旨在解决传统车损检测中效率低、精度不高的问题。该系统利用YOLOV8的目标检测能力&#xff0c;在单张图像上实现了车身损坏区域的精确识别和分类&#xff0c;尤其是在车身凹痕、车身裂纹和车身划…

【前端学习笔记】Javascript学习二(运算符、数组、函数)

一、运算符 运算符&#xff08;operator&#xff09;也被称为操作符&#xff0c;是用于实现赋值、比较和执行算数运算等功能的符号。 JavaScript中常用的运算符有&#xff1a; 算数运算符、递增和递减运算符、比较运算符、逻辑运算符、赋值运算符 算数运算符&#xff1a; 、-…

python实战案例----使用 PyQt5 构建简单的 HTTP 接口测试工具

python实战案例----使用 PyQt5 构建简单的 HTTP 接口测试工具 文章目录 python实战案例----使用 PyQt5 构建简单的 HTTP 接口测试工具项目背景技术栈用户界面核心功能实现结果展示完整代码总结 在现代软件开发中&#xff0c;测试接口的有效性与响应情况变得尤为重要。本文将指导…

网络安全之信息收集-实战-1

请注意&#xff0c;本文仅供合法和授权的渗透测试使用&#xff0c;任何未经授权的活动都是违法的。 实战&#xff1a;补天公益src“吉林通用航空职业技术学院” 奇安信&#xff5c;用户登录https://www.butian.net/Loo/submit?cid64918 域名或ip&#xff1a;https://www.jlth…