线程池的优点:

  • 重用线程,减少线程创建和销毁的性能开销。
  • 管理线程,并提供定时执行以及指定间隔循环执行等功能。

Android中的线程来源于Java中的Executor,实现类是ThreadPoolExecutor,ThreadPoolExecutor通过构造方法的一系列参数,来构成不同配置的线程池。常用的构造方法有下面四个:

ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue) 
ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory)
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
    核心线程数,默认情况核心线程会一直存活,即使处于闲置状态也不会受keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。
  • maximumPoolSize
    线程池所能容纳的最大线程数。
  • keepAliveTime
    非核心线程的闲置超时时间,超过这个时间就会被回收。如果allowCoreThreadTimeOut设置为true,核心线程也会被回收。
  • unit
    keepAliveTime的单位
  • workQueue
    线程池中的任务队列。常用的有四种队列:SynchronousQueue, LinkedBlockingDeque, ArrayBlockingQueue, DelayQueue。
  • threadFactory
    线程工厂,提供创建新线程的功能。通过线程工厂可以对线程的一些属性进行定制。
  • RejectedExecutionHandler
    当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution方法。

线程池的任务队列(重要)

下面介绍上面说到的常用的四种队列,在这之前先根据上面的参数分析一下帮助理解:有核心线程、非核心线程、任务队列三个角色,他们可以根据当前配置,对每个新来的任务做出处理(处理就是要么分配线程去执行任务,要么把任务存到任务队列里等待分配线程)。那么,三个角色可以设计如下:
有限个核心线程 + 任务队列(队列大小可配置)+ 无限个非核心线程
(确切的说是corePoolSize个核心线程,maximumPoolSize - corePoolSize个非核心线程)

不难理解,任务很多的时候,核心线程不够用了就存到任务队列里,队列存满了,就创建非核心线程执行任务(就像节假日客运站临时加车一样)。这样设计包含了两种极端情况,就是队列大小是0和队列无限大,所以具体设计如下三种情况:

  1. 有限个核心线程+无限个非核心线程,那么来一个新任务就能给它分配一个线程去执行,这个时候任务队列大小是0。
  2. 有限个核心线程+任务队列(队列无限大),那么来一个新任务可以给它分配线程去执行,无线程分配可以存到队列里。
  3. 有限个核心线程+任务队列(队列大小有限且不是0)+无限个非核心线程。

这三种设计,就对应了workQueue参数的三种队列:SynchronousQueue, LinkedBlockingDeque, ArrayBlockingQueue

SynchronousQueue

收到新任务时,直接交给线程处理,如果所有线程都在工作,那么新建线程来处理这个任务。如果将maximumPoolSize指定成Integer.MAX_VALUE,就是无限个线程。
(对应上面的第1种设计)

LinkedBlockingDeque

这个队列没有大小限制。当接收到新任务时,如果当前线程小于核心线程数,则新建核心线程处理任务。如果当前线程数等于核心线程数,则进入队列等待。由于队列没有大小限制,也就导致了 maximumPoolSize 的设定失效,因为这时最大线程数不会超过核心线程数。
(对应上面第2种设计)

ArrayBlockingQueue

可以限定队列长度,收到任务的时候,如果没有达到corePoolSize的值,则新建核心线程执行任务,如果达到了,入队等候。如果队列已满,则新建非核心线程执行任务。如果达到了maximumPoolSize,就会发生错误。
(对应上面第3种设计)

DelayQueue

队列内元素必须实现 Delayed 接口,这就意味着你传进去的任务必须先实现 Delayed 接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。

现实中,队列不可能无限大,非核心线程不可能有无数个,那么上面几种队列就有超出线程总数的情况,这种情况只需要配置ThreadPoolExecutor的第7个参数RejectedExecutionHandler即可(可以翻到上面重新看下参数说明)。


4种常见的线程池

下面介绍4种系统提供的配置好的线程池,当然,如果理解了上面的任务队列,自己配置出相同的线程池是很简单的。这4种线程池使用系统的工具类Executors来创建,如下:

//下面加了参数1的是因为没有无参数的重载方法。
Executors.newFixedThreadPool(1);
Executors.newCachedThreadPool();
Executors.newScheduledThreadPool(1);
Executors.newSingleThreadExecutor();
FixedThreadPool

对应:有限个核心线程+任务队列(队列无限大) --> LinkedBlockingQueue
它的创建方法需要传入参数来指定核心线程数。功能特点很好理解,下面是艺术探索中对它的描述:

它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭。当所有线程都处于活动状态时,新任务就会处于等待状态,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界请求。通过newFixedThreadPool方法的源码可以发现FixedThreadPool中只有核心线程没有超时机制,另外任务队列也是没有大小限制的。

CachedThreadPool

对应:有限个核心线程+无限个非核心线程 --> SynchronousQueue
它的核心线程数是0,所以它的创建方法不需要参数,下面是艺术探索中对它的描述:

它是一种线程数量不定的线程池,它只有非核心线程,并且最大线程数为Integer.MAX_VALUE。由于Integer.MAX_VALUE是一个很大的数,实际上就相当于最大线程数可以任意大。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲线程来处理新任务。线程池中的空闲线程都有超时机制,超时时长是60秒,超过60秒闲置的线程就会被回收。和FixedThreadPool不同的是,CachedThreadPool的任务队列其实相当于一个空集合,这将导致任何任务都会被立即执行。这类线程池比较适合执行大量的耗时较少的任务。当整个线程池处于闲置状态时,所有线程都会超时而被停止,这个时候CachedThreadPool中实际上是没有任何线程的,它几乎是不占任何系统资源的。

ScheduledThreadPool

对应:DelayQueue
它的创建方法需要参数来指定核心线程数,描述如下:

它的核心线程数是固定的,而非核心线程数是没有限制的。这个线程池主要用于执行定时任务和有固定周期的重复任务。

SingleThreadExecutor

对应:有限个核心线程+任务队列(队列无限大) --> LinkedBlockingQueue
与FixedThreadPool的区别在于,SingleThreadExecutor只有一个核心线程,所以它的创建方法无需参数。描述如下:

这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。意义在于统一所有外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。

最后是使用线程池的几个示例:

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        SystemClock.sleep(2000);
    }
};

//自己配置的线程池
ThreadPoolExecutor myExecutor = new ThreadPoolExecutor(2, 10, 5, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
//执行myRunnable任务
myExecutor.execute(myRunnable);

//系统提供的线程池
ExecutorService fixedExecutor = Executors.newFixedThreadPool(1);
//执行myRunnable任务
fixedExecutor.execute(myRunnable);

//系统提供的线程池
ScheduledExecutorService sExecutor = Executors.newScheduledThreadPool(1);
//2000ms后执行myRunnable
sExecutor.schedule(myRunnable, 2000, TimeUnit.MILLISECONDS);
//10ms后,每隔1000ms执行一次myRunnable
sExecutor.scheduleAtFixedRate(myRunnable, 10, 1000, TimeUnit.MILLISECONDS);
文章参考

《Android开发艺术探索》
Java多线程-线程池ThreadPoolExecutor构造方法和规则
线程池,这一篇或许就够了

Java-线程池总结的更多相关文章

  1. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  2. Java线程池使用说明

    Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...

  3. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  4. Java线程池的那些事

    熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor.大家可能了解到它的原理,甚至看过它的源码:但 ...

  5. 四种Java线程池用法解析

    本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...

  6. Java线程池的几种实现 及 常见问题讲解

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...

  7. Java线程池应用

    Executors工具类用于创建Java线程池和定时器. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程.在任意点,在大多数 nThread ...

  8. Java线程池的原理及几类线程池的介绍

    刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...

  9. Java线程池与java.util.concurrent

    Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行 ...

  10. [转 ]-- Java线程池使用说明

    Java线程池使用说明 原文地址:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1. ...

随机推荐

  1. 设置eclipse中的${user}

    打开eclipse根目录找到eclipse.ini文件增加初始配置: -Duser.name=snzigod@hotmail.com 重启eclipse后${user}变量的值就变成了snzigod@ ...

  2. java append方法

    JAVA 中 Stringbuffer 有append()方法  Stringbuffer其实是动态字符串数组  append()是往动态字符串数组添加,跟“xxxx”+“yyyy”相当那个‘+’号  ...

  3. STL学习笔记1--vector

    C++STL(Standard Template Library)标准模板库是通用类模板和算法的集合.包含一些标准的数据结构的实现如queues(队列),lists(链表),stacks(栈)等.ST ...

  4. day03_06 变量详解

    print ("hello world") print("alex") print("jinxing") print("3乘以4= ...

  5. [git 学习篇]自己在github创建一个远程服务器创库

    现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举 ...

  6. [python篇][其他] python博客学习汇总

    http://blog.csdn.net/zhangxinrun/article/details/8141913

  7. 九度oj 题目1187:最小年龄的3个职工

    题目描述: 职工有职工号,姓名,年龄.输入n个职工的信息,找出3个年龄最小的职工打印出来. 输入: 输入第一行包括1个整数N,1<=N<=30,代表输入数据的个数. 接下来的N行有N个职工 ...

  8. 九度oj 题目1376:最近零子序列

    题目描述: 给定一个整数序列,你会求最大子串和吗?几乎所有的数据结构与算法都会描述求最大子串和的算法.今天让大家来算算最近0子串和,即整数序列中最接近0的连续子串和.例如,整数序列6, -4, 5, ...

  9. 设计模式(一)单例模式:实现 Serializable 接口之后的额外操作

    思想: 一个单例类,无论采取哪一种设计(单元素枚举类除外), 一旦间接或者直接实现 Serializable 接口,为了保证单例,就要多增加一点考虑:保证类在反序列化之后能够保证单例. public ...

  10. 【bzoj1307】玩具 单调栈

    题目描述 小球球是个可爱的孩子,他喜欢玩具,另外小球球有个大大的柜子,里面放满了玩具,由于柜子太高了,每天小球球都会让妈妈从柜子上拿一些玩具放在地板上让小球球玩. 这天,小球球把所有的N辆玩具摆成一排 ...