什么是线程池

我们都知道线程是什么,但是一提到线程池,给人的第一个感觉就是一堆线程,这样的理解其实不太对,线程池可以只有一个线程,也可以有多个线程,而线程池最大的作用是管理和复用线程。一提到管理线程,我们都知道线程池可以帮我们创建线程,也可以帮我们销毁线程。但是一提到复用线程,相信大多数人都愣了,要知道 Thread.start 执行完 Runnable.run 方法之后线程就自动停止了,也就是说 Thread 对象只能调用一次 start 方法,那么在这种情况下线程池是如何复用线程的呢?

都说源码是最好的老师,接下来让我们通过源码来看看这葫芦里面卖的什么药

线程池的原理

我们还是从线程池的写法入手源码

在线程池的执行方法中,我们发现了一个出现频率很高的 API,接下来让我们看看这句代码干了什么事

我们可以看到,线程池拿到任务之后丢给了 Worker 类处理

进 Worker 类一看,不看不知道,一看吓一跳,这个类还创建了 Thread 对象

有一个非常重要细节,就是创建线程的时候,传入的并不是我们给线程池的那个 Runnable 对象,而是 Worker 对象本身,也就是说线程 start 的时候,Worker 类的 run 方法会被执行

在这里可以看到我们刚刚传入 Runnable 对象,然后开启了一个 while 循环,循环的意思是:只要 task 对象不为空,那么就会一直调用 task = getTask(),直到获取到的 task 对象为空了才会停止循环

那么 getTask 方法里面到底干了什么,让我们接着看

通过这两段代码,我们可以得知,getTask 其实就是往阻塞队列中取出 Runnable 对象

通过这些,我们可以得出,线程池复用线程的原理,创建 Thread 对象的时候传入的不是我们的 Runnable 对象,而是通过线程池自定义的 Runnable 类,这个类主要的作用不仅是执行我们的 Runnable 对象,当我们传入的任务被某个线程执行完毕之后,它还会遍历阻塞队列中其他未执行的任务,这样就能达到一个线程执行多个 Runnable 对象的效果,这个就是线程池复用线程的原理。

什么时候该用线程池

通过源码我们了解到了线程池的工作机制,那么问题来了,什么情况下该用线程池,什么情况下不该用线程池?

这个问题其实很简单,源码已经告诉我们答案了,当线程池中只有一个线程并且只执行一次任务的时候,我们可以考虑不用线程池,直接创建 Thread 对象来执行这个任务。

也就是说线程池的生命周期只有单个任务的情况下,没有任何优势可言,但是如果在多任务同时并发的情况下,线程池是可以帮我们减少线程数量的,用一句最简单的话来理解就是,用最少的人力干完所有的活,人太少活太多不行,人太多活太少也不行。

线程池核心参数

接下来让我们讲讲创建线程池的几个核心参数

corePoolSize 是核心线程数,何为核心线程数?源码注释已经写得很明白了,也就是最小线程数,规定线程池里面最少必须有几个线程在工作,这些核心线程在没有任务可以执行的时候还必须存活着,除非我们设定了核心线程的存活时间,否则这些核心线程永远不会停止工作。

maximumPoolSize 是最大线程数,这里面不仅包含了核心线程数,还包含了非核心线程数,那么问题来了,何为非核心线程?

这不得不来场比较了,核心线程和非核心线程最大的区别是:核心线程在没有任务的情况下不会被回收,而非核心线程一旦没有了任务就会被回收。

举一个生活中最常见的例子,我们如果把核心线程比作一个正式工,那么非核心线程就是一个外包工。正式工没活干没事,但如果外包工没活干了的话是要面临被裁员的。

workQueue 是阻塞队列,为什么要用队列(Queue),因为队列是先进先出,先进来的任务先取出,最终先进来的任务最先执行完毕的可能性就大,但这还得考虑任务具体的耗时情况而定,在耗时相同的情况下,先进来的任务就先执行完毕。当然这个队列还有其他用处,那就是存放一些未执行的任务,具体有什么作用,可以让我们来一场实验。

实验场景:核心线程数 = 1 ,最大线程数 = 3,开启 for 循环执行 10 个任务,任务内容:sleep 1 秒并打印当前线程名

当阻塞队列容量无限大时,10 个任务只出现 1 个线程在排队执行

当阻塞队列容量设置为 10 个或者 9 个时,10 个任务也是只出现 1 个线程在排队执行

当阻塞队列容量设置为 8 个时,10 个任务出现了 2 个线程在并发执行

当阻塞队列容量设置为 7 个时,10 个任务出现了 3 个线程在并发执行

当阻塞队列容量设置为 6 个时,线程池抛出异常,表示拒绝执行任务

实验结论:往线程池添加一个新的任务时,如果核心线程处于空闲状态,任务会直接交由核心线程处理,否则任务会存放到阻塞队列中,当阻塞队列中的任务数量超过设定的最大值时,才会开启非核心线程去执行,如果当前任务总量 > 阻塞队列的最大容量 + 最大线程数时,线程池则会拒绝执行该任务。

keepAliveTime 是非核心线程的存活时间,当线程池中的非核心线程没有任务执行的时候,如果超过了指定的时间还是没有执行任何任务的时候,那么这个非核心线程会在超时后被回收掉,如果我们不指定这个时间,那么这些非核心线程将永远不会被回收。

其他参数不是那么重要,这里直接略过不讲,接下来简单介绍一下系统 API 给我们提供的四种线程池。

系统提供的四种线程池

这里创建了一个核心线程数和最大线程数都为 1 的线程池,简单理解这个线程池只有一个线程,正如它的方法名一样(new Single Thread Executor)

这个线程池跟上一个线程池非常像,都是核心线程数和非核心线程数都是用同一个数值,只不过上一个线程池是写死的 1,而这个线程池可以自定义这个数值。

这个线程池没有核心线程数,也没有限制最大线程数,那么可以得出这个线程池里面的线程都是非核心线程,并且还规定了非核心线程的存活时间不能超过 60 秒。

最后一个线程池的特点是:核心线程数是固定的,但不限制最大线程数,非核心线程的闲置时间不能超过 10 毫秒。

了解过后才发现,这四种线程池无非是核心线程数、最大线程数、非核心线程的存活时间这几个参数的定义上徘徊

Android技术分享Q群:78797078

android 进程池 参数,Android 线程池全解析相关推荐

  1. 为什么用线程池?解释下线程池参数?线程池处理流程?阻塞队列的作用?为什么是先添加列队而不是先创建最大线程?线程池中线程复用原理?

    为什么用线程池?解释下线程池参数? 1.降低资源消耗:提高线程利用率,降低创建和销毁线程的消耗. 2.提高响应速度:任务来了,直接有线程可用可执行,而不是先创建线程,再执行. 3.提高线程的可管理性: ...

  2. java线程池参数_java线程池参数设置原则,如何设置线程池参数比较合理?

    线程池的参数应该怎样设置呢?相信对于很多的人来说这也是一个比较难的问题,下面就让我们一起来解决一下,究竟应该如何设置线程池的参数才是最合理的吧! 首先在设置参数的时候,有以下的几点是我们需要考虑到的! ...

  3. 可动态调节参数的线程池实现

    背景 线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽.在高并发的任务处理场景,线程池的使用是必不可少的.在双11主图价格表达项目中为了提升处 ...

  4. 【操作系统】操作系统知识点整理;C++ 实现线程池与windows 线程池的使用;

    文章目录 体系结构 冯诺依曼 存储结构 cache常见的组织结构 cache命中 缓存一致性 硬中断.软中断 操作系统结构 内核 Linux宏内核 内存管理 虚拟内存 内存管理 - 分段 - 分页 - ...

  5. 由浅入深 学习 Android Binder(十一) binder线程池

    Android Binder系列文章: 由浅入深 学习 Android Binder(一)- AIDL 由浅入深 学习 Android Binder(二)- bindService流程 由浅入深 学习 ...

  6. Android并发之Executor(线程池)家族(二)之AtomicInteger

    线程并发,那就牵扯到内存共享的问题,在并发编程中,有三个理念:原子性.可见性.有序性.这里分享一个转载. 转载:Java volatile关键字最全总结:原理剖析与实例讲解(简单易懂) 在Thread ...

  7. 【Android 异步操作】线程池 ( Worker 简介 | 线程池中的工作流程 runWorker | 从线程池任务队列中获取任务 getTask )

    文章目录 一.线程池中的 Worker ( 工作者 ) 二.线程池中的工作流程 runWorker 三.线程池任务队列中获取任务 getTask 在博客 [Android 异步操作]线程池 ( 线程池 ...

  8. Android性能优化之使用线程池处理异步任务

    说到线程,我想大家都不陌生,因为在开发时候或多或少都会用到线程,而通常创建线程有两种方式: 1.继承Thread类 2.实现Runnable接口 虽说这两种方式都可以创建出一个线程,不过它们之间还是有 ...

  9. 【Android 异步操作】线程池 ( 线程池简介 | 线程池初始化方法 | 线程池种类 | AsyncTask 使用线程池示例 )

    文章目录 一.线程池简介 二.线程池初始化方法简介 三.线程池使用示例 一.线程池简介 线程池一般是实现了 ExecutorService 接口的类 , 一般使用 ThreadPoolExecutor ...

  10. 【Android 异步操作】线程池 ( 线程池作用 | 线程池种类 | 线程池工作机制 | 线程池任务调度源码解析 )

    文章目录 一.线程池作用 二.线程池种类 三.线程池工作机制 四.线程池任务调度源码解析 一.线程池作用 线程池作用 : ① 避免创建线程 : 避免每次使用线程时 , 都需要 创建线程对象 ; ② 统 ...

最新文章

  1. 谷歌提出“T5” 新NLP模型,突破迁移学习局限,多基准测试达SOTA!
  2. 方式程0day MS17-010远程溢出漏洞测试
  3. Android调用系统照相机
  4. easycode 表配置_idea的easyCode的 MybatisPlus模板的配置详解
  5. android开发微博前的包准备,新浪微博开发之前期准备篇
  6. javascript数据结构之队列
  7. 一条语句引发的思考:装箱和拆箱,空指针的类型转换
  8. python全栈-Day 6
  9. 初识BoundSQL
  10. 利用Aspose.PDF for .NET实现pdf转word
  11. python怎么读音发音英语翻译-python style是什么意思
  12. Python使用阿里API进行情感分析
  13. T00ls内部旁注扫描器.rar
  14. TVS二极管DO-15封装型号,有哪些?
  15. 大学计算机实验报告虚拟机,安装虚拟机的实验报告(共10篇).docx
  16. 让你秒懂的Lambda表达式超级详细讲解
  17. 中文分词算法之--最大匹配法
  18. 如何在EXCEL中画横线并输入汉字
  19. java.lang.NoSuchMethodException: com.xxx.xxx.xxxinit()
  20. ascii码,gbk编码,unicodo码,utf-8码

热门文章

  1. Linux宝库名人轶事栏目 | 我与中国开源软件二十年(三)
  2. 【单目标优化求解】基于matlab被囊群算法(TSA)求解最优目标问题【含Matlab源码 1567期】
  3. 【图像融合】基于matlab GUI拉普拉斯金字塔+小波变换+NSCT图像融合【含Matlab源码 870期】
  4. 【滤波器】基于matlab GUI低通+带通+高通FIR与IIR滤波器设计【含Matlab源码 360期】
  5. python建模预测_如何使用Python进行节目观众数的线性回归预测
  6. pycharm快捷键之①“上下移动某一行“②参数提示
  7. linux系统交换分区的文件格式是,【简答题】Red Hat Linux中,交换分区的文件系统类型是什么,光盘文件的文件系统类型是什么?...
  8. 计算机显示桌面的按钮,如何找回Windows“显示桌面”按钮 -电脑资料
  9. 三位数除以两位数竖式计算没有余数_北京版二年级数学下册第一单元有余数的除法练习题【都有电子版】...
  10. 在一个成熟行业里怎么生存下去?