【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.mzph.cn/web/60116.shtml

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

相关文章

本地音乐服务器(三)

6. 删除音乐模块设计 6.1 删除单个音乐 1. 请求响应设计 2. 开始实现 首先在musicmapper新增操作 Music findMusicById(int id);int deleteMusicById(int musicId); 其次新增相对应的.xml代码&#xff1a; <select id"findMusicById" resultType"com.exa…

Spring Boot图书馆管理系统:疫情中的管理利器

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了疫情下图书馆管理系统的开发全过程。通过分析疫情下图书馆管理系统管理的不足&#xff0c;创建了一个计算机管理疫情下图书馆管理系统的方案。文章介绍了疫情下图…

集群聊天服务器(7)数据模块

目录 Mysql数据库代码封装头文件与源文件 Mysql数据库代码封装 业务层代码不要直接写数据库&#xff0c;因为业务层和数据层的代码逻辑也想完全区分开。万一不想存储mysql&#xff0c;想存redis的话&#xff0c;就要改动大量业务代码。解耦合就是改起来很方便。 首先需要安装m…

Linux第93步_Linux内核的LED灯驱动

Linux内核的LED灯驱动采用platfomm框架&#xff0c;因此我们只需要按照要求在“设备树文件”中添加相应的LED节点即可。 1 、通过“linux内核图形化配置界面”令“CONFIG_LEDS_GPIOy” 1)、打开终端&#xff0c;输入“cd linux/atk-mp1/linux/my_linux/linux-5.4.31/回车”&a…

Zmap+python脚本+burp实现自动化Fuzzing测试

声明 学习视频来自 B 站UP主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 ✍&#x1f3fb;作者简介&#xff1a;致…

红外相机和RGB相机外参标定 - 无需标定板方案

1. 动机 在之前的文章中红外相机和RGB相机标定&#xff1a;实现两种模态数据融合_红外相机标定-CSDN博客 &#xff0c;介绍了如何利用标定板实现外参标定&#xff1b;但实测下来发现2个问题&#xff1a; &#xff08;1&#xff09;红外标定板尺寸问题&#xff0c;由于标定板小…

android:taskAffinity 对Activity退出时跳转的影响

android:taskAffinity 对Activity跳转的影响 概述taskAffinity 的工作机制taskAffinity对 Activity 跳转的影响一个实际的开发问题总结参考 概述 在 Android 开发中&#xff0c;任务栈&#xff08;Task&#xff09;是一个核心概念。它决定了应用程序的 Activity 如何相互交互以…

Ubuntu常见命令

关于export LD_LIBRARY_PATHcmake默认地址CMakelists.txt知识扩充/home&#xff1a;挂载新磁盘到 /home 子目录 关于export LD_LIBRARY_PATH 程序运行时默认的依赖库的位置包括lib, /usr/lib ,/usr/local/lib 通过命令export LD_LIBRARY_PATHdesired_path:$LD_LIBRARY_PATH追加…

时间类的实现

在现实生活中&#xff0c;我们常常需要计算某一天的前/后xx天是哪一天&#xff0c;算起来十分麻烦&#xff0c;为此我们不妨写一个程序&#xff0c;来减少我们的思考时间。 1.基本实现过程 为了实现时间类&#xff0c;我们需要将代码写在3个文件中&#xff0c;以增强可读性&a…

php交友源码交友系统源码相亲交友系统源码php社交系统php婚恋源码php社区交友源码vue 仿交友社交语聊技术栈

关于PHP交友、相亲、婚恋、社区交友系统的源码以及Vue仿交友社交语聊技术栈&#xff0c;以下是一些详细信息和建议&#xff1a; 一、PHP交友系统源码 系统架构设计 前端展示层&#xff1a;负责向用户提供直观友好的界面&#xff0c;包括注册登录页面、个人资料页面、匹配页面、…

Java小技艺

使用bat文件启动jar包 平时在工作中运行jar包一般是导出后命令行窗口运行 jar -jar xxx.jar 这个其实是很不方便的。可以在win上编写bat脚本去运行jar包的。 1 编写bat脚本 start jre8/bin/javaw -jar xxxx.jar2 将jre和待执行的jar包存放到一个目录下(和bat文件在同一目录…

蓝桥杯第22场小白入门赛2~5题

这场比赛开打第二题就理解错意思了&#xff0c;还以为只能用3个消除和5个消除其中一种呢&#xff0c;结果就是死活a不过去&#xff0c;第三题根本读不懂题意&#xff0c;这蓝桥杯的题面我只能说出的是一言难尽啊。。第四题写出来一点但是后来知道是错了&#xff0c;不会正解&am…

‘视’不可挡:OAK相机助力无人机智控飞行!

南京邮电大学通达学院的刘同学用我们的oak-d-lite实现精确打击无人机的避障和目标识别定位功能&#xff0c;取得了比赛冠军。我们盼望着更多的朋友们能够加入到我们OAK的队伍中来&#xff0c;参与到各式各样的比赛中去。我们相信&#xff0c;有了我们相机的助力&#xff0c;大家…

最小生成树——Kruskal、Prim算法

图的存储&#xff1a; 高阶数据结构——图 文章目录 目录 文章目录 一、kruskal算法 二、Prim算法 前言 连通图中的每一棵生成树&#xff0c;都是原图的一个极大无环子图&#xff0c;即&#xff1a;从其中删去任何一条边&#xff0c;生成树 就不在连通&#xff1b;反之&#xf…

集群聊天服务器(9)一对一聊天功能

目录 一对一聊天离线消息服务器异常处理 一对一聊天 先新添一个消息码 在业务层增加该业务 没有绑定事件处理器的话消息会派发不出去 聊天其实是服务器做一个中转 现在同时登录两个账号 收到了聊天信息 再回复一下 离线消息 声明中提供接口和方法 张三对离线的李…

华为再掀技术革新!超薄膜天线设计路由器首发!

随着Wi-Fi技术的不断进步&#xff0c;新一代的Wi-Fi 7路由器凭借其高速率、低延迟、更稳定的性能受到了广泛关注。它能够更好地满足现代家庭对网络性能的高要求&#xff0c;带来更加流畅、高效的网络体验。9月24日&#xff0c;华为在其秋季全场景新品发布会上推出了全新Wi-Fi 7…

【阅读记录-章节2】Build a Large Language Model (From Scratch)

目录 2.Working with text data2.1 Understanding word embeddings2.2 Tokenizing text通过一个简单的实验来理解文本的词元化概念关键概念 2.3 Converting tokens into token IDs实现分词器类&#xff08;Tokenizer Class&#xff09;应用分词器测试文本的编码与解码通过分词器…

SDF,一个从1978年运行至今的公共Unix Shell

关于SDF 最近发现了一个很古老的公共Unix Shell服务器&#xff0c;这个项目从1978年运行至今&#xff0c;如果对操作系统&#xff0c;对Unix感兴趣&#xff0c;可以进去玩一玩体验一下 SDF Public Access UNIX System - Free Shell Account and Shell Access 注册方式 我一…

关于Qt C++中connect的几种写法

目录 1. 传统的槽函数写法 2. 使用函数指针的connect写法&#xff08;5.0&#xff09; 3. Lambda表达式作为槽函数&#xff08;C11&#xff09; 4.使用QOverload选择重载信号的写法 这connect函数就像是编程世界里的“茴”字&#xff0c;千变万化&#xff0c;各有千秋。咱们…

反向代理模块

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当于…