前言:

上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己来自定义一个线程池,今天来学习一下ThreadPoolExecutor,然后结合使用场景定义一个按照线程优先级来执行的任务的线程池。

线程管理相关文章地址:

ThreadPoolExecutor

ThreadPoolExecutor线程池用于管理线程任务队列、若干个线程。

1.)ThreadPoolExecutor构造函数
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,RejectedExecutionHandler handler)
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler)
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory, RejectedExecutionHandler handler)

  corePoolSize: 线程池维护线程的最少数量
  maximumPoolSize:线程池维护线程的最大数量
  keepAliveTime: 线程池维护线程所允许的空闲时间
  unit: 线程池维护线程所允许的空闲时间的单位
  workQueue: 线程池所使用的缓冲队列
  threadFactory:线程池用于创建线程
  handler: 线程池对拒绝任务的处理策略

2.)创建新线程

默认使用Executors.defaultThreadFactory(),也可以通过如下方式

    /**
* 创建线程工厂
*/
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1); @Override
public Thread newThread(Runnable runnable) {
return new Thread(runnable, "download#" + mCount.getAndIncrement());
}
};
3.)线程创建规则

ThreadPoolExecutor对象初始化时,不创建任何执行线程,当有新任务进来时,才会创建执行线程。构造ThreadPoolExecutor对象时,需要配置该对象的核心线程池大小和最大线程池大小

  1. 当目前执行线程的总数小于核心线程大小时,所有新加入的任务,都在新线程中处理。
  2. 当目前执行线程的总数大于或等于核心线程时,所有新加入的任务,都放入任务缓存队列中。
  3. 当目前执行线程的总数大于或等于核心线程,并且缓存队列已满,同时此时线程总数小于线程池的最大大小,那么创建新线程,加入线程池中,协助处理新的任务。
  4. 当所有线程都在执行,线程池大小已经达到上限,并且缓存队列已满时,就rejectHandler拒绝新的任务。

4.)默认的RejectExecutionHandler拒绝执行策略

  1. AbortPolicy 直接丢弃新任务,并抛出RejectedExecutionException通知调用者,任务被丢弃
  2. CallerRunsPolicy 用调用者的线程,执行新的任务,如果任务执行是有严格次序的,请不要使用此policy
  3. DiscardPolicy 静默丢弃任务,不通知调用者,在处理网络报文时,可以使用此任务,静默丢弃没有几乎处理的报文
  4. DiscardOldestPolicy 丢弃最旧的任务,处理网络报文时,可以使用此任务,因为报文处理是有时效的,超过时效的,都必须丢弃

我们也可以写一些自己的RejectedExecutionHandler,例如拒绝时,直接将线程加入缓存队列,并阻塞调用者,或根据任务的时间戳,丢弃超过限制的任务。

5.)任务队列BlockingQueue

排队原则

  1. 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。

  2. 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

  3. 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

常见几种BlockingQueue实现

1. ArrayBlockingQueue :  有界的数组队列
  2. LinkedBlockingQueue : 可支持有界/无界的队列,使用链表实现
  3. PriorityBlockingQueue : 优先队列,可以针对任务排序
  4. SynchronousQueue : 队列长度为1的队列,和Array有点区别就是:client thread提交到block queue会是一个阻塞过程,直到有一个worker thread连接上来poll task。

6.)线程池执行

  execute()方法中,调用了三个私有方法
  addIfUnderCorePoolSize():在线程池大小小于核心线程池大小的情况下,扩展线程池
  addIfUnderMaximumPoolSize():在线程池大小小于线程池大小上限的情况下,扩展线程池
  ensureQueuedTaskHandled():保证在线程池关闭的情况下,新加入队列的线程也能正确处理

7.)线程池关闭

  shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
  shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

ThreadPoolExecutor实现优先级线程池

1.)定义线程优先级枚举
/**
* 线程优先级
*/
public enum Priority {
HIGH, NORMAL, LOW
}
2.)定义线程任务
/**
* 带有优先级的Runnable类型
*/
/*package*/ class PriorityRunnable implements Runnable { public final Priority priority;//任务优先级
private final Runnable runnable;//任务真正执行者
/*package*/ long SEQ;//任务唯一标示 public PriorityRunnable(Priority priority, Runnable runnable) {
this.priority = priority == null ? Priority.NORMAL : priority;
this.runnable = runnable;
} @Override
public final void run() {
this.runnable.run();
}
}
3.)定义一个PriorityExecutor继承ThreadPoolExecutor
public class PriorityExecutor extends ThreadPoolExecutor {

    private static final int CORE_POOL_SIZE = 5;//核心线程池大小
private static final int MAXIMUM_POOL_SIZE = 256;//最大线程池队列大小
private static final int KEEP_ALIVE = 1;//保持存活时间,当线程数大于corePoolSize的空闲线程能保持的最大时间。
private static final AtomicLong SEQ_SEED = new AtomicLong(0);//主要获取添加任务 /**
* 创建线程工厂
*/
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1); @Override
public Thread newThread(Runnable runnable) {
return new Thread(runnable, "download#" + mCount.getAndIncrement());
}
}; /**
* 线程队列方式 先进先出
*/
private static final Comparator<Runnable> FIFO = new Comparator<Runnable>() {
@Override
public int compare(Runnable lhs, Runnable rhs) {
if (lhs instanceof PriorityRunnable && rhs instanceof PriorityRunnable) {
PriorityRunnable lpr = ((PriorityRunnable) lhs);
PriorityRunnable rpr = ((PriorityRunnable) rhs);
int result = lpr.priority.ordinal() - rpr.priority.ordinal();
return result == 0 ? (int) (lpr.SEQ - rpr.SEQ) : result;
} else {
return 0;
}
}
}; /**
* 线程队列方式 后进先出
*/
private static final Comparator<Runnable> LIFO = new Comparator<Runnable>() {
@Override
public int compare(Runnable lhs, Runnable rhs) {
if (lhs instanceof PriorityRunnable && rhs instanceof PriorityRunnable) {
PriorityRunnable lpr = ((PriorityRunnable) lhs);
PriorityRunnable rpr = ((PriorityRunnable) rhs);
int result = lpr.priority.ordinal() - rpr.priority.ordinal();
return result == 0 ? (int) (rpr.SEQ - lpr.SEQ) : result;
} else {
return 0;
}
}
}; /**
* 默认工作线程数5
*
* @param fifo 优先级相同时, 等待队列的是否优先执行先加入的任务.
*/
public PriorityExecutor(boolean fifo) {
this(CORE_POOL_SIZE, fifo);
} /**
* @param poolSize 工作线程数
* @param fifo 优先级相同时, 等待队列的是否优先执行先加入的任务.
*/
public PriorityExecutor(int poolSize, boolean fifo) {
this(poolSize, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE, fifo ? FIFO : LIFO), sThreadFactory);
} public PriorityExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
} /**
* 判断当前线程池是否繁忙
* @return
*/
public boolean isBusy() {
return getActiveCount() >= getCorePoolSize();
} /**
* 提交任务
* @param runnable
*/
@Override
public void execute(Runnable runnable) {
if (runnable instanceof PriorityRunnable) {
((PriorityRunnable) runnable).SEQ = SEQ_SEED.getAndIncrement();
}
super.execute(runnable);
}
}

里面定义了两种线程队列模式: FIFO(先进先出) LIFO(后进先出) 优先级相同的按照提交先后排序

4.)测试程序
 ExecutorService executorService = new PriorityExecutor(5, false);
for (int i = 0; i < 20; i++) {
PriorityRunnable priorityRunnable = new PriorityRunnable(Priority.NORMAL, new Runnable() {
@Override
public void run() {
Log.e(TAG, Thread.currentThread().getName()+"优先级正常");
}
});
if (i % 3 == 1) {
priorityRunnable = new PriorityRunnable(Priority.HIGH, new Runnable() {
@Override
public void run() {
Log.e(TAG, Thread.currentThread().getName()+"优先级高");
}
});
} else if (i % 5 == 0) {
priorityRunnable = new PriorityRunnable(Priority.LOW, new Runnable() {
@Override
public void run() {
Log.e(TAG, Thread.currentThread().getName()+"优先级低");
}
});
}
executorService.execute(priorityRunnable);
}

运行结果:不难发现优先级高基本上优先执行了 最后执行的基本上优先级比较低

Android线程管理之ThreadPoolExecutor自定义线程池的更多相关文章

  1. Android线程管理(一)——线程通信

    线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...

  2. ThreadPoolExecutor自定义线程池

    1.ThreadPoolExecutor创建线程池的构造函数 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long ...

  3. python---基础知识回顾(十)进程和线程(py2中自定义线程池和py3中的线程池使用)

    一:自定义线程池的实现 前戏: 在进行自定义线程池前,先了解下Queue队列 队列中可以存放基础数据类型,也可以存放类,对象等特殊数据类型 from queue import Queue class ...

  4. ACE线程管理机制-面向对象的线程类ACE_Task

    转载于:http://www.cnblogs.com/TianFang/archive/2006/12/05/583231.html 我们在前一章中使用ACE_Thread包装时,你一定已经注意到了一 ...

  5. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  6. Android线程管理之ThreadLocal理解及应用场景

    前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...

  7. Android线程管理之Thread使用总结

    前言 最近在一直准备总结一下Android上的线程管理,今天先来总结一下Thread使用. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Executo ...

  8. Android线程管理之AsyncTask异步任务

    前言: 前面几篇文章主要学习了线程以及线程池的创建与使用,今天来学习一下AsyncTask异步任务,学习下AsyncTask到底解决了什么问题?然而它有什么弊端?正所谓知己知彼百战百胜嘛! 线程管理相 ...

  9. 基于ThreadPoolExecutor,自定义线程池简单实现

    一.线程池作用 在上一篇随笔中有提到多线程具有同一时刻处理多个任务的特点,即并行工作,因此多线程的用途非常广泛,特别在性能优化上显得尤为重要.然而,多线程处理消耗的时间包括创建线程时间T1.工作时间T ...

随机推荐

  1. pt-table-checksum

    pt-table-checksum是percona公司提供的一个用于在线比对主从数据一致性的工具. 实现原理 将一张大表分成多个chunk,每次针对一个chunk进行校验,同时将校验的结果通过REPL ...

  2. MVVM TextBox的键盘事件

    MVVM下RichTextBox的键盘回车事件设置为发送,不是回车 xmlns:i="http://schemas.microsoft.com/expression/2010/interac ...

  3. 再部署一个 instance 和 Local Network - 每天5分钟玩转 OpenStack(131)

    上一节部署了 cirros-vm1 到 first_local_net,今天我们将再部署 cirros-vm2 到同一网络,并创建 second_local_net. 连接第二个 instance 到 ...

  4. 如何将VCSA添加到微软域控环境,并且实现微软域账号登陆vCenter

    v:* { } o:* { } w:* { } .shape { } p.msonormal,li.msonormal,div.msonormal { margin: 0cm; margin-bott ...

  5. margin折叠-从子元素margin-top影响父元素引出的问题

    正在做一个手机端电商项目,顶部导航栈的布局是一个div包含一个子div,如果给在正常文档流中的子div一个垂直margin-top,神奇的现象出现了,两父子元素的边距没变,但父div跟着一起往下走了! ...

  6. NodeJS使用mysql

    1.环境准备 手动添加数据库依赖: 在package.json的dependencies中新增, "mysql" : "latest", { "nam ...

  7. MySQL:常见使用问题

    内容 1.Linux 上安装 MySQL 2.单机上安装多实例 3.不知root密码情况下,修改root密码 1.Linux 上安装MySQL 安装步骤: 1)解压 tar.gz文件 -linux-g ...

  8. liunx 磁盘管理命令记录

    Linux磁盘管理好坏管理直接关系到整个系统的性能问题. Linux磁盘管理常用三个命令为df.du和fdisk. df:列出文件系统的整体磁盘使用量 du:检查磁盘空间使用量 fdisk:用于磁盘分 ...

  9. ASP.NET Core 在 JSON 文件中配置依赖注入

    前言 在上一篇文章中写了如何在MVC中配置全局路由前缀,今天给大家介绍一下如何在在 json 文件中配置依赖注入. 在以前的 ASP.NET 4+ (MVC,Web Api,Owin,SingalR等 ...

  10. Linux学习日记-WCF RestFul的部署(三)

    一.关于WCF 的部署 默认的wshttp风格的wcf是很容易部署上去的,但是这里给个建议尽量不要使用WCF的配置文件去部署尽管 我们都已经很熟悉了,在使用配置文件你会发现各种蛋疼的问题. 二.WCF ...