multiThread我爱编程

Android线程池深入理解

2018-05-27  本文已影响137人  xiaoguan

1 基本概念

2 线程池的优点

3 理解线程池

3.1 构造方法

ThreadPoolExecutor有多个构造方法,都需要一些参数,主要构造方法有:

public ThreadPoolExecutor(
          int corePoolSize,
          int maximumPoolSize,
          long keepAliveTime,
          TimeUnit unit,
          BlockingQueue<Runnable> workQueue,
          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, 
keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);
}                    

3.2 线程池大小

线程池的大小主要与四个参数有关:

3.3 队列

ThreadPoolExecutor要求的队列类型是阻塞队列BlockingQueue,它们都可以用作线程池的队列,比如:

3.4 任务拒绝策略

如果队列有界,且maximumPoolSize有限,则当队列排满,线程个数也达到了maximumPoolSize,这时新任务来了,如何处理呢?此时,会触发线程池的任务拒绝策略。需要强调下,拒绝策略只有在队列有界,且maximumPoolSize有限的情况下才会触发。
  默认情况下,提交任务的方法如execute/submit/invokeAll等会抛出异常,类型为RejectedExecutionException。不过,拒绝策略是可以自定义的,ThreadPoolExecutor实现了四种处理方式:

4 线程池的类型及区别

类Executors提供了一些静态工厂方法,可以方便的创建一些预配置的线程池,主要方法有:

(1) newSingleThreadExecutor:只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此适用于需要确保所有任务被顺序执行的场合。

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

只使用一个线程,使用无界队列LinkedBlockingQueue,线程创建后不会超时终止,该线程顺序执行所有任务。

(2)newFixedThreadPool:线程固定,且不会被回收,能够更快的响应外界请求。比较适合在系统负载高下,通过队列对新任务排队,保证有足够的资源处理实际的任务

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

使用固定数目的n个线程,使用无界队列LinkedBlockingQueue,线程创建后不会超时终止。和newSingleThreadExecutor一样,由于是无界队列,如果排队任务过多,可能会消耗非常大的内存。

(3)newCachedThreadPool:核心线程为0,非核心线程数量相当于无限大,任何任务都会被立即执行。比较适合在系统负载不太高下,执行大量的执行时间比较短的任务

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

它的corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,keepAliveTime是60秒,队列为SynchronousQueue。含义是,当新任务到来时,如果正好有空闲线程在等待任务,则其中一个空闲线程接受该任务,否则就总是创建一个新线程,创建的总线程个数不受限制。对任一空闲线程,如果60秒内没有新任务,就终止。

(4) ScheduledThreadPool:核心线程数量固定,非核心线程数量不定的线程池。适合执行定时任务或者具有周期性的重复任务

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, 
          MILLISECONDS,
          new DelayedWorkQueue());
}

ScheduledThreadPool的核心线程数量是固定的,由传入的corePoolSize参数决定,非核心线程数量可以无限大。非核心线程闲置回收的超时时间为10秒( DEFAULT_KEEPALIVE_MILLIS的值为10L)。

5 阿里Android手册的强制要求

线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方
式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。Executors 返回的线程池对象的弊端如下:

//正例
//返回可用处理器的Java虚拟机的数量
int NUMBER_OF_CORES = 
Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new 
LinkedBlockingQueue<Runnable>();

ExecutorService executorService = new ThreadPoolExecutor(
    NUMBER_OF_CORES, 
    NUMBER_OF_CORES*2,                                                               
    KEEP_ALIVE_TIME, 
    KEEP_ALIVE_TIME_UNIT,                                           
    taskQueue, 
    new BackgroundThreadFactory(), 
    new DefaultRejectedExecutionHandler());
//反例
ExecutorService cachedThreadPool = 
Executors.newCachedThreadPool();

6 总结

ThreadPoolExecutor实现了生产者/消费者模式,工作者线程就是消费者,任务提交者就是生产者,线程池自己维护任务队列。当我们碰到类似生产者/消费者问题时,应该优先考虑直接使用线程池,而非重新发明轮子,自己管理和维护消费者线程及任务队列。

7 参考链接

计算机程序的思维逻辑 (78) - 线程池

Android 线程池的类型、区别以及为何要用线程池

阿里Anddroid开发手册

上一篇下一篇

猜你喜欢

热点阅读