线程池的优点:

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

ThreadPoolExecutor:

Android中,用ThreadPoolExecutor来实现线程池的配置。
ThreadPoolExecutor文档中文版
ThreadPoolExecutor文档英文版

QQ截图20160711141312.png

ThreadPoolExecutor的构造方法

ThreadPoolExecutor的构造方法有四个,其实现如下:
```
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
} public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
} public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
```
构造方法的参数
  • corePoolSize
    程池中的核心线程数,也就是是线程池中的最小线程数;
    核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出;

  • maximumPoolSize
    最大线程池大小,当活动线程数达到这个值,后续任务会被阻塞

  • keepAliveTime
    线程池中超过corePoolSize数目的非核心线程最大存活时间;闲置时的超时时长,超过这个值后,闲置线程就会被回收

  • unit
    keepAliveTime 参数的时间单位。这是一个枚举,详情请参考TimeUnit

  • workQueue
    执行前用于保持任务的队列,也就是线程池的缓存队列。此队列仅保持由 execute 方法提交的 Runnable 任务
    关于三种提交策略这篇文章不错

  • threadFactory
    线程工厂,为线程池提供创建新线程的功能,它是一个接口,只有一个方法:Thread newThread(Runnable r)

  • RejectedExecutionHandler
    线程池对拒绝任务的处理策略。一般是队列已满或者无法成功执行任务,这时ThreadPoolExecutor会调用handler的rejectedExecution方法来通知调用者
    ThreadPoolExecutor默认有四个拒绝策略:

      1、ThreadPoolExecutor.AbortPolicy()   直接抛出异常RejectedExecutionException
    2、ThreadPoolExecutor.CallerRunsPolicy() 直接调用run方法并且阻塞执行
    3、ThreadPoolExecutor.DiscardPolicy() 直接丢弃后来的任务
    4、ThreadPoolExecutor.DiscardOldestPolicy() 丢弃在队列中队首的任务

也可以自己继承RejectedExecutionHandler来写拒绝策略.

ThreadPoolExecutor的执行过程:

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

  1. 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程
  2. 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
  3. 当提交任务数超过【maximumPoolSize+阻塞队列大小】时,新提交任务由RejectedExecutionHandler处理 (关于这里,网上90%以上的人说当任务数>=maximumPoolSize时就会被拒绝,我不知道依据在哪里,也不知道代码验证过没,经过我的验证这种说法是不成立的,具体的看下边日志分析)
  4. 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
  5. 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
定制自己的线程池:
public class ThreadTestActivity extends AppCompatActivity {
private final int CORE_POOL_SIZE = 1;//核心线程数
private final int MAX_POOL_SIZE = 3;//最大线程数
private final int BLOCK_SIZE = 2;//阻塞队列大小
private final long KEEP_ALIVE_TIME = 2;//空闲线程超时时间
private ThreadPoolExecutor executorPool; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread_test);
//创建线程池
// 创建一个核心线程数为3、最大线程数为8,缓存队列大小为5的线程池
executorPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(BLOCK_SIZE),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
executorPool.allowCoreThreadTimeOut(true);
} public void begin(View view) {
for (int num = 0; num < 6; num++) {//每个500ms添加一个任务到队列中
try {
Li("execute");// 监听相关数据
executorPool.execute(new WorkerThread("thread-" + num));
} catch (Exception e) {
Log.e("threadtest", "AbortPolicy...");
}
} // 20s后,所有任务已经执行完毕,我们在监听一下相关数据
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(20 * 1000);
} catch (Exception e) { }
Li("monitor after");
}
}).start();
} private void Li(String mess) {
Log.i("threadtest", "monitor " + mess
+ " CorePoolSize:" + executorPool.getCorePoolSize()
+ " PoolSize:" + executorPool.getPoolSize()
+ " MaximumPoolSize:" + executorPool.getMaximumPoolSize()
+ " ActiveCount:" + executorPool.getActiveCount()
+ " TaskCount:" + executorPool.getTaskCount() );
} } // 模拟耗时任务
public class WorkerThread implements Runnable {
private String threadName;
public WorkerThread(String threadName) {
this.threadName = threadName;
}
@Override
public synchronized void run() { int i = 0;
boolean flag = true;
try {
while (flag) {
Thread.sleep(1000);
i++;
Log.e("threadtest", "WorkerThread " + threadName + " " + i);
if (i >2) flag = false;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getThreadName() {
return threadName;
}
}

日志信息:

QQ截图20160712161004.png

下面就来对日志信息进行分析:

  1. 上边粉色部分(1~6行),可以看到poolsize逐渐累加,一直加到最大线程数后不再增加,这呼应了上述“执行过程1”
  2. 接下来绿色部分()7行,验证了上边我们说的“执行过程3”,缓存队列数为2,最大线程数为3,共有6条任务,所以会有【 6-(2+3)】条任务被拒绝,这里拒绝策略我们用的是ThreadPoolExecutor.AbortPolicy()也就是直接抛出异常,也就是我们日志的第7行
  3. 然后8~22行是任务的执行过程,
  4. 其中蓝色部分(8~16)行,我们可以看到有3条任务在同时执行,也就是最大线程数
  5. 接下来的绿色(17~22行),在三条任务执行完成后,剩余的排队任务才开始执行
  6. 最后,23行,20s后,线程都处于空闲状态,所以非核心线程会被回收,但是因为代码中我们设置了executorPool.allowCoreThreadTimeOut(true),所以这时处于空闲状态的核心线程也会被回收,这时池中的线程数为0
关于线程池的一些建议
  • 最大线程数一般设为2N+1最好,N是CPU核数

官方定义的四种线程池

其实,本应该先说官方定义的这四种线程池,然后再说自定义线程池,但是考虑到里边的一些配置参数,所以本帖先利用自定义线程池把各个配置参数理一下,然后再讲官方定义的四种线程池,这样也便于理解官方定义的这四种线程池
这四种线程池都是通过Executors的工厂方法来实现

1、FixedThreadPool

他是一种数量固定的线程池,且任务队列也没有大小限制;
它只有核心线程,且这里的核心线程也没有超时限制,所以即使线程处于空闲状态也不会被回收,除非线程池关闭;
当所有的任务处于活动状态,新任务都处于等待状态,知道所有线程空闲出来;
因为它不会被回收,所以它能更快的响应;
源码:

    public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

实现:

    ExecutorService service = Executors.newFixedThreadPool(3);
service.execute(new WorkerThread("thread-" + num));
2、CachedThreadPool

无界线程池,可以进行自动线程回收
他是一种线程数量不固定的线程池;
它只有非核心线程,且最大线程数为Integer.MAX_VALUE,也就是说线程数可以任意大;
当池中的线程都处于活动状态时,会创建新的线程来处理任务,否则会利用空闲线程来处理任务;所以,任何添加进来的任务都会被立即执行;
池中的空闲线程都有超时限制,为60s,超过这个限制就会被回收,当池中的所有线程都处于闲置状态时,都会因超时而被回收,这个时候,她几乎不占用任何系统资源;
适合做大量的耗时较少的任务;
源码:

    public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

实现:

    ExecutorService service = Executors.newCachedThreadPool();
service.execute(new WorkerThread("thread-"));
3、SingleThreadExecutor

只有一个核心线程,所有任务都在同一线程中按序执行,这样也就不需要处理线程同步的问题;
源码:

    public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

实现:

    ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new WorkerThread("thread-"));
4、ScheduledThreadPool

它的核心线程数量是固定的,而非核心线程是没有限制的,且非核心线程空闲时会被回收;
适合执行定时任务和具有固定周期的任务
源码:

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

实现:

    ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);

ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(); threadPool.schedule(runnable, 20, TimeUnit.SECONDS);// 20秒后执行任务

threadPool.scheduleAtFixedRate(runnable,10,20,TimeUnit.SECONDS);//延迟10s,每20s执行一次任务

由于本人技术有限,避免不了出现一些错误或者理解有偏差描述不清楚的地方,请大家谅解并提醒我:)

上一篇:AsyncTask
再来一篇:java中的thread

更多内容请关注我的Android专题
本文出自:http://www.jianshu.com/users/c1b4a5542220/latest_articles
转载请注明出处!


作者:乆_丩
链接:http://www.jianshu.com/p/3da543063b8c
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android中的线程池 ThreadPoolExecutor的更多相关文章

  1. android中的线程池学习笔记

    阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...

  2. Android中的线程池

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

  3. Android中的线程池概述

    线程池 Android里面,耗时的网络操作,都会开子线程,在程序里面直接开过多的线程会消耗过多的资源,在众多的开源框架中也总能看到线程池的踪影,所以线程池是必须要会把握的一个知识点; 线程运行机制 开 ...

  4. android中对线程池的理解与使用

    前段时间有幸接到腾讯上海分公司的 Android开发面试,虽然最后一轮被毙了.但还是得总结一下自己在android开发中的一些盲点,最让我尴尬的是面试官问我几个android中线程池的使用与理解..哎 ...

  5. android开发学习 ------- 【转】 android中的线程池

    线程很常见 , https://blog.csdn.net/seu_calvin/article/details/52415337    参考,保证能看懂.

  6. Android开发之线程池使用总结

    线程池算是Android开发中非常常用的一个东西了,只要涉及到线程的地方,大多数情况下都会涉及到线程池.Android开发中线程池的使用和Java中线程池的使用基本一致.那么今天我想来总结一下Andr ...

  7. Android线程池ThreadPoolExecutor

    阿里巴巴Android开发手册[强制]新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor或者其他形式自定义的线程池),不允许在应用中自行显式创建线程说明:使 ...

  8. android线程池ThreadPoolExecutor的理解

    android线程池ThreadPoolExecutor的理解 线程池 我自己理解看来.线程池顾名思义就是一个容器的意思,容纳的就是ThreadorRunable, 注意:每一个线程都是需要CPU分配 ...

  9. java 中的线程池

    1.实现下面的一个需求,控制一个执行函数只能被五个线程访问 package www.weiyuan.test; public class Test { public static void main( ...

随机推荐

  1. codefoeces 671 problem D

    D. Roads in Yusland standard output Mayor of Yusland just won the lottery and decided to spent money ...

  2. Django【进阶】权限管理

    一.权限 RBAC:role basic access control   1.什么是权限: 一个权限就是一个url,不同个权限对应不同的url,拥有权限即可以访问这个url.   2.权限划分: 如 ...

  3. css3 不常用但重要的属性

    IOS 2.-webkit-touch-callout (ios 2.0+) none:禁止弹出系统弹窗 default:默认 Android Common 1.-webkit-user-select ...

  4. javascript的有效校验

    //年月日期有效性检验 function yearAndMonthCheck() { var flag = true; var currentyear = new Date().getFullYear ...

  5. 1.flume概述

    我们的web服务器等等每天会产生大量的日志,我们要把这些日志收集起来,移动到hadoop平台上进行分析. 那么如何移动这些数据呢?一种方法是通过shell cp到hadoop集群上,然后通过hdfs ...

  6. BZOJ 3038 上帝造题的七分钟二

    无题目 但是百度会发现题目和3211基本一致 所以看上一篇博文的上一篇博文呢

  7. MATLAB求解方程与方程组

    1.      solve函数 ①求解单个一元方程的数值解 syms x; x0 = double(solve(x +2 - exp(x),x)); 求x+2 = exp(x)的解,结果用double ...

  8. AC日记——[SHOI2008]小约翰的游戏John bzoj 1022

    1022 思路: nim: 代码: #include <cstdio> #include <cstdlib> #include <iostream> #includ ...

  9. c#实现Form窗体始终在桌面最前端显示

    方法一 //调用API [System.Runtime.InteropServices.DllImport("user32", CharSet = System.Runtime.I ...

  10. Python爬虫Scrapy测试

    # -*- coding:utf- -*- import urllib import urllib2 import re import thread import time #糗事百科爬虫类 clas ...