最近发现很多人对于安卓的线程池的理解会比较低
本准备自己写一篇的,但是发现了一篇总结的比较全的博客
所以转载过来分享给大家
文章出自君宝,有些地方做了修改,请作者原谅
文章最下方有我自己自定义的线程池,只为探索原理
希望有所帮助

线程池简介

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
比如:一个应用要和网络打交道,有很多步骤需要访问网络,为了不阻塞主线程,每个步骤都创建个线程,在线程中和网络交互,用线程池就变的简单,线程池是对线程的一种封装,让线程用起来更加简便,只需要创一个线程池,把这些步骤像任务一样放进线程池,在程序销毁时只要调用线程池的销毁函数即可。

安卓常用的线程池有三种:

newFixedThreadPool

创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
  ExecutorService pool = Executors.newFixedThreadPool(2);
  //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
  Thread t1 = new MyThread();
  Thread t2 = new MyThread();
  Thread t3 = new MyThread();
  Thread t4 = new MyThread();
  Thread t5 = new MyThread();
  //将线程放入池中进行执行
  pool.execute(t1);
  pool.execute(t2);
  pool.execute(t3);
  pool.execute(t4);
  pool.execute(t5);

newSingleThreadExecutor

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。

newCachedThreadPool

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。

其余两种线程池

newScheduledThreadPool

这种线程池可以控制线程池内的线程定时,或者周期性的执行某个任务,创建的时候需要指定他的核心线程数,它的最大线程数是int类型的最大值

 ScheduledExecutorService newScheduledThreadPool =  Executors.newScheduledThreadPool(3);// 参数1:任务// 参数2:延迟时长// 参数3:延迟时长的时间单位newScheduledThreadPool.schedule(new TestRunnable(1), 200, TimeUnit.SECONDS);

newSingleThreadScheduledExecutor

核心线程数为1,用法与上面相同,区别类似于newFixedThreadPool和newSingleThreadExecutor的关系

核心原理

安卓提供的所有线程池方式均是通过以下这个方法提供的

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

corePoolSize: 线程池维护线程的最少数量 - 核心线程数 (核心线程会随着线程池的创建而创建出来 通常为CPU核心数+1)
maximumPoolSize:线程池维护线程的最大数量 - 最大线程数 (线程池中所能容纳线程数量的上限, 它一定大于等于核心线程)
keepAliveTime: 线程池维护线程所允许的空闲时间 - 存活时间 (当工作线程没有任务执行,且空闲时间已经到达这个存货时间的时候,会被销毁)
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列 h
andler: 线程池对拒绝任务的处理策略

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法欲添加到线程池时:

l. 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2. 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5. 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。
线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。
每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。
为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的 Executors 工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。否则,在手动配置和调整此类时,使用以下指导:
核心和最大池大小
ThreadPoolExecutor 将根据 corePoolSize(参见 getCorePoolSize())和 maximumPoolSize(参见 getMaximumPoolSize())设置的边界自动调整池大小。当新任务在方法execute(Java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。
按需构造
默认情况下,即使核心线程最初只是在新任务需要时才创建和启动的,也可以使用方法 prestartCoreThread() 或 prestartAllCoreThreads() 对其进行动态重写。
创建新线程
使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个 ThreadGroup 中一律使用 Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread 返回null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。
保持活动时间
如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止(参见 getKeepAliveTime(java.util.concurrent.TimeUnit))。这提供了当池处于非活动状态时减少资源消耗的方法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit) 动态地更改此参数。使用 Long.MAX_VALUE TimeUnit.NANOSECONDS 的值在关闭前有效地从以前的终止状态禁用空闲线程。
排队
所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
A. 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。
B. 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
C. 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

排队有三种通用策略:

正常为5种

第一种:LinkedBlockingQueue 无界任务队列
第二种:ArrayBlockingQueue 有界任务队列
第三种:DelayQueue 等待队列,他会让工作队列里的任务等待一会(自定义时间)再进入线程池
第四种:SynchronousQueue 直接提交任务队列
第五种:PriorityBlockingQueue 优先级任务队列,可以指定任务的优先级,优先级比较高的先进入

直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
被拒绝的任务
当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:

A. 在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
B. 在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
C. 在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。
D. 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。
定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

挂钩方法

此类提供 protected 可重写的 beforeExecute(java.lang.Thread, java.lang.Runnable) 和 afterExecute(java.lang.Runnable, java.lang.Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化 ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法 terminated() 来执行 Executor 完全终止后需要完成的所有特殊处理。
如果挂钩或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。

队列维护

方法 getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(java.lang.Runnable) 和 purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。

自定义线程池

自定义线程池类MyThreadPool


package com.example.wjn.threadpool;import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** Created by wjn on 15/5/5.*/
public class MyThreadPool {// 线程池对象private ThreadPoolExecutor threadPoolExecutor;public MyThreadPool() {// 获取CPU 核心数int CPUCores = Runtime.getRuntime().availableProcessors();// 线程池对象初始化// 具体参数详情看博客上方对于这个方法的介绍threadPoolExecutor = new ThreadPoolExecutor(CPUCores, CPUCores * 2 + 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());}public void execute(Runnable runnable) {threadPoolExecutor.execute(runnable);}
}

MainActivity中的代码

// 自定义线程池MyThreadPool myThreadPool = new MyThreadPool();
myThreadPool.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000L);Log.d("Log", "1");} catch (InterruptedException e) {e.printStackTrace();}}});myThreadPool.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000L);Log.d("Log", "2");} catch (InterruptedException e) {e.printStackTrace();}}});

基本上就是这些知识,希望能在安卓的线程池相关内容上对各位有所帮助。

Android开发-线程池相关推荐

  1. Android开发线程池管理类之ThreadPoolExecutor工具类

    平常开发中网络请求自从Android4.0后强制要求放到子线程中避免主线程进行网络请求,一般很多人习惯直接通过new Thread的方法.不建议这样使用.我们用ThreadPoolExecutor线程 ...

  2. Android应用线程池最大线程数量

    Android应用线程池最大线程数量 线程池的大小经验值一般这样设置:(其中N为CPU的核数)  如果是CPU密集型应用,则线程池大小设置为N+1  如果是IO密集型应用,则线程池大小设置为2N+1 ...

  3. android自定义线程池工具类,妈妈再也不用担心你不会使用线程池了(ThreadUtils)...

    为什么要用线程池 使用线程池管理线程有如下优点:降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行. 提高线程的可 ...

  4. 【Android】线程池

    在Android中,主线程不能执行耗时的操作,否则可能会导致ANR.那么,耗时操作应该在其它线程中执行.线程的创建和销毁都会有性能开销,创建过多的线程也会由于互相抢占系统资源而导致阻塞的现象.这个时候 ...

  5. Android 自定义线程池的实战

    前言:在上一篇文章中我们讲到了AsyncTask的基本使用.AsyncTask的封装.AsyncTask 的串行/并行线程队列.自定义线程池.线程池的快速创建方式. 对线程池不了解的同学可以先看 An ...

  6. Android之线程池

    http://www.trinea.cn/android/java-android-thread-pool/ 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是 ...

  7. android asynctask 线程池,AsyncTask和线程池

    在Android中,UI操作是线程不安全的,如果想要在子线程中进行UI操作,或者在主线程中进行耗时操作,则需要借助Android的异步消息处理机制.但我们需要开启N个线程的时候,我们可以这样做: 直接 ...

  8. Android开发 - 线程和服务

    服务吧,在程序即便关闭的时候还是可以回后台运行,不搞情怀了.后台功能属于四大组件之一. 服务是Android中实现程序后台运行的解决方案,很适合执行不需要与用户交互而且长时间运行的任务.不依赖于任何U ...

  9. Android线程池封装库

    目录介绍 1.遇到的问题和需求 1.1 遇到的问题有哪些 1.2 遇到的需求 1.3 多线程通过实现Runnable弊端 1.4 为什么要用线程池 2.封装库具有的功能 2.1 常用的功能 3.封装库 ...

最新文章

  1. 彻底搞懂Java内存泄露
  2. 需要添加什么头文件_速冻水饺为什么需要食品添加剂?
  3. 我在学python-你们以为我在学C++?其实我在学Python!人生苦短!
  4. 全球与中国立式胶体磨市场发展现状调研及前景需求分析报告2022-2027年版
  5. 必看:Kubernetes 开发环境对比
  6. List、Array与ArrayList
  7. 视频PPT互动问答丨数据库智能运维专题
  8. 编码格式与Datatable数据
  9. MapReduce Shuffle详解
  10. visual studio运行时库MT、MTd、MD、MDd的区别
  11. 最新PS2022下载含安装操作步骤
  12. elasticsearch 基础 —— Parent-Child 父子关系(5.X老版本)
  13. xp无法访问win7计算机,xp连接win7共享打印机无法连接
  14. OSChina 周六乱弹 —— 泡面就要泡着吃……
  15. CorelDRAW2022最新电脑版离线安装教程
  16. 如何测试一个串口调试助手软件,串口调试助手(SComAssistant)
  17. 互联网日报 | 5月18日 星期二 | 中国移动启动A股上市;京东物流启动全球招股;快手发布首款自研手游产品...
  18. 姜小白的Python日记Day15 系统模块详解2 sys模块和os模块
  19. LaTeX技巧013:quotation,quote环境实现首行缩进2em
  20. WordPress初学者入门教程-WordPress的网页

热门文章

  1. android开发和手游开发工具,developer盘点Android开发者必备十大开发工具
  2. yum错误:Cannot retrieve repository metadata (repomd.xml) for repository解决方法
  3. 注册中心集群 服务负载均衡 雪崩效应Hystrix
  4. 【修复日常bug】微信小程序canvas画商品海报出现个别用户无法生成的情况
  5. 够迫履门夹钾灼敛墒套谮姑韩立对墨大夫一年后是否真的信守承诺,很是怀疑,若真是像对方所说
  6. buffer pool详解(free链表+flush链表+lru链表)
  7. 测试您的杀毒软件的代码(不是病毒)
  8. 【参观微软分享有礼】微软拥抱开源
  9. 豆瓣电影的API接口豆瓣电影详情即将上映新片榜短影评
  10. 华云数据加入上海信息技术应用创新联盟!