前言:

自从 2007 年起 iPhone 和 Android 手机的相继问世,以及 2013 年 4G 网络的正式商用,使得在全球范围内催生了全新的 “移动互联网” 时代。

这个时代打从一开始就与互联网产生紧密联系,通过移动互联网,我们得以尝试许多不同以往在 PC 端上做的事,例如

上街买菜时,我们可以扫码解锁共享单车,可以给摆摊的老板扫码支付;
工作生活中,可以在通勤路上刷短视频、可以在午餐时间点外卖;
外出旅游时,可以通过社交软件分享一路上拍摄的美景等等。

Java线程池采用了享元设计模式,在系统中维持一定数量的线程,用于处理异步或并发需求,在平时处理异步或并发任务时被广泛使用。这里基于JDK1.8和Android28来整理一些关于线程池的知识点。本篇主要包含如下内容:

android多线程工厂 多线程 android_java

一、合理使用线程池的好处

(1)降低资源消耗。 重用线程池,可以降低频繁创建和销毁线程所带来的消耗。

(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。如果:T1 + T3 远大于 T2,则可以采用线程池,以提高程序的性能。线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高程序性能的。它把T1,T3分别安排在程序的启动和结束的时间段或者一些空闲的时间段,这样在程序处理客户请求时,不会有T1,T3的开销了

(3)提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还可能导致大量的线程之间因互相抢占系统资源而导致阻塞。使用线程池可以进行统一分配、调优和监控。

二、Jdk提供的线程池框架

Java中提供的线程池框架中,主要类的关系及结构如下图所示:

android多线程工厂 多线程 android_android多线程工厂_02

  • Executor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离开来。
  • ExecutorService接口继承了Executor,在其上做了一些shutdown()、submit()的扩展,可以说是真正的线程池接口;
  • AbstractExecutorService抽象类实现了ExecutorService接口中的大部分方法;
  • ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务。
  • ScheduledExecutorService接口继承了ExecutorService接口,提供了带"周期执行"功能ExecutorService;
  • ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令,或者定期执行命令。ScheduledThreadPoolExecutor比Timer更灵活,功能更强大。

三、线程池创建中各个参数的含义

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

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor中的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime和unit所指定的时长决定,超过这个时长后核心线程就会被终止。

在默认情况下,当提交一个任务时,线程池会创建一个新线程执行任务,直到当前线程数等于corePoolSize,而如果执行了ThreadPoolExecutor的prestartAllCoreThreads()方法,线程会提前创建并启动所有核心线程;如果当前线程数为corePoolSize,继续提交的任务会被保存到阻塞队列中,等待被执行。

  • maximumPoolSize

线程池所能容纳的最大线程数。当阻塞队列(即参数workQueue)满了后,如果继续提交任务,则创建新的线程执行任务,直到该线程池中所有活动的线程数达到maximumPoolSize。如果线程池中的活动线程数达到maximumPoolSize,后续还有新任务,就会执行线程池的拒绝策略,由另外一个参数RejectedExecutionHandler来确定。

  • keepAliveTime

非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。默认情况下,该参数只在线程数大于corePoolSize时才有用,而当ThreadPoolExecutor中的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。

  • unit

用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等。

  • workQueue

线程池的阻塞队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。通过workQueue,线程池实现了阻塞功能。常用阻塞队列有:

1)ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不保证线程公平的访问队列,所谓公平访问队列是指阻塞的线程,可以按照阻塞的先后顺序访问队列,即先阻塞线程先访问队列。非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格,有可能先阻塞的线程最后才访问队列。初始化时有参数可以设置

2)LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列(常用)。此队列的默认和最大长度为Integer.MAX_VALUE,它按照先进先出的原则对元素进行排序。

3)PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。

4)DelayQueue:一个使用优先级队列实现的无界阻塞队列,支持延时获取元素,队列使用PriorityQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。DelayQueue非常有用,可以将DelayQueue运用在缓存系统的设计  可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。

5)SynchronousQueue:一个不存储元素的阻塞队列(常用)。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合传递性场景。

6)LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

7)LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

以上的阻塞队列都实现了BlockingQueue接口,也都是线程安全的。

有界队列就是长度有限,满了以后生产者会阻塞,无界队列就是里面能放无数的东西而不会因为队列长度限制被阻塞,当然空间限制来源于系统资源的限制,如果处理不及时,导致队列越来越大越来越大,超出一定的限制致使内存超限,操作系统或者JVM帮你解决烦恼,直接把你 OOM kill 省事了。无界也会阻塞,为何?因为阻塞不仅仅体现在生产者放入元素时会阻塞,消费者拿取元素时,如果没有元素,同样也会阻塞。

一般来说,我们应该尽量使用有界队列,因为使用无界队列作为工作队列会对线程池带来如下影响:

1)当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize。

2)由于1,使用无界队列时maximumPoolSize将是一个无效参数。

3)由于1和2,使用无界队列时keepAliveTime将是一个无效参数。

4)更重要的,使用无界queue可能会耗尽系统资源,有界队列则有助于防止资源耗尽,同时即使使用有界队列,也要尽量控制队列的大小在一个合适的范围。

  • threadFactory

线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口:

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名,当然还可以更加自由的对线程做更多的设置,比如设置所有的线程为守护线程。Executors静态工厂里默认的threadFactory,线程的命名规则是“pool-数字-thread-数字”。

//java.util.concurrent.Executors.java
 2 static class DefaultThreadFactory implements ThreadFactory {
 3     private static final AtomicInteger poolNumber = new AtomicInteger(1);
 4     private final ThreadGroup group;
 5     private final AtomicInteger threadNumber = new AtomicInteger(1);
 6     private final String namePrefix;
 7     DefaultThreadFactory() {
 8         SecurityManager s = System.getSecurityManager();
 9         group = (s != null) ? s.getThreadGroup() :
10                               Thread.currentThread().getThreadGroup();
11         namePrefix = "pool-" +
12                       poolNumber.getAndIncrement() +
13                      "-thread-";
14     }
15     public Thread newThread(Runnable r) {
16         Thread t = new Thread(group, r,
17                               namePrefix + threadNumber.getAndIncrement(),
18                               0);
19         if (t.isDaemon())
20             t.setDaemon(false);
21         if (t.getPriority() != Thread.NORM_PRIORITY)
22             t.setPriority(Thread.NORM_PRIORITY);
23         return t;
24     }
25 }
  • handler

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

(1)AbortPolicy:直接抛出RejectedException异常,默认策略;

(2)CallerRunsPolicy:用调用者所在的线程来执行任务;

(3)DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;

(4)DiscardPolicy:直接丢弃任务;

当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

四、线程池的工作机制

1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

2)如果运行的线程等于corePoolSize后仍然有任务提交,则将任务加入BlockingQueue。

3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务,其上限是当前活动线程数不超过maximumPoolSize。

4)如果创建新线程时,当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

五、提交任务

execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。

submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

六、关闭线程池

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow方法

七、 合理地配置线程池

要想合理地配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析。

(1)任务的性质:CPU密集型任务、IO密集型任务和混合型任务。

(2)任务的优先级:高、中和低。

(3)任务的执行时间:长、中和短。

(4)任务的依赖性:是否依赖其他系统资源,如数据库连接。

性质不同的任务可以用不同规模的线程池分开处理。

CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1(这为什么有个+1呢?这是因为在现代计算机中引入了虚拟内存技术,cpu读取真实内存时可能该数据不在真实内存中,需要到虚拟内存中去取,这叫内存缺页。而cpu读取虚拟内存的速度远小于读取真实内存的速度,此时cpu只能等待。+1之后多了一个线程,当出现内存缺页时,正在等待的cpu可以去执行这多出来的线程,从而提高cpu的使用率)个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。

混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。

优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先执行。

执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间短的任务先执行。

建议使用有界队列。有界队列能增加系统的稳定性和预警能力,可以根据需要设大一点儿,比如几千。如果当时我们设置成无界队列,那么线程池的队列就会越来越多,有可能会撑满内存,导致整个系统不可用,而不只是后台任务出现问题。

八、JDK中内置的4类线程池

在JDK中的Executors类中提供了4类已经配置好的线程池:

android多线程工厂 多线程 android_android多线程工厂_03

其源码为:

1 //java.util.concurrent.Executors.java
 2 public class Executors {
 3     ......
 4     //提供的默认的ThreadFactory
 5     public static ThreadFactory defaultThreadFactory() {
 6         return new DefaultThreadFactory();//在前文中有该类的代码
 7     }
 8     ......
 9     public static ExecutorService newCachedThreadPool() {
10         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
11                                       60L, TimeUnit.SECONDS,
12                                       new SynchronousQueue<Runnable>());
13     }
14     ......
15     public static ExecutorService newFixedThreadPool(int nThreads) {
16         return new ThreadPoolExecutor(nThreads, nThreads,
17                                   0L, TimeUnit.MILLISECONDS,
18                                   new LinkedBlockingQueue<Runnable>());
19     }
20     ......
21     public static ExecutorService newSingleThreadExecutor() {
22         return new FinalizableDelegatedExecutorService
23             (new ThreadPoolExecutor(1, 1,
24                                 0L, TimeUnit.MILLISECONDS,
25                                 new LinkedBlockingQueue<Runnable>()));
26     }
27     ....
28 }
29 
30 public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
31     ....
32     public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
33     return new ScheduledThreadPoolExecutor(corePoolSize);
34     }
35     ....
36     public ScheduledThreadPoolExecutor(int corePoolSize) {
37     super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
38           new DelayedWorkQueue());
39     }
40     ....
41 }
42 
43 public class ThreadPoolExecutor extends AbstractExecutorService {
44     ......
45     public ThreadPoolExecutor(int corePoolSize,
46                           int maximumPoolSize,
47                           long keepAliveTime,
48                           TimeUnit unit,
49                           BlockingQueue<Runnable> workQueue) {
50         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
51          Executors.defaultThreadFactory(), defaultHandler);
52     }
53     ......
54 }

1、CachedThreadPool线程池

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

通过Executors的newCachedThreadPool方法来创建。它是一种线程数量不定的线程池它只有非核心线程,并且其最大线程数为IntegerMAX_VALUE。由于IntegerMAX VALUE是一个很大的数,实际上就相当于最大线程数可以任意大。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收。和FixedThreadPool不同的是,CachedThreadPool的任务队列其实相当于一个空集合。这将导致任何任务都会立即被执行,因为在这种场景下SynchronousQueue是无法插入任务的。SynchronousQueue是一个非常特殊的队列,在很多情况下可以把它简单理解为一个无法存储元素的队列,由于它在实际中较少使用,这里就不深入探讨它了。从 CachedThreadPool的特性来看,这类线程池比较适合执行大量的耗时较少的任务。当整个线程池都处于闲置状态时,线程池中的线程都会超时而被停止,这个时候CachedThreadPool之中实际上是没有任何线程的,它几乎是不占用任何系统资源的。

测试示例:

1 public static void testNewCachedThreadPool() throws InterruptedException {
 2     ExecutorService executorService = Executors.newCachedThreadPool();
 3     for (int i = 1; i < 10; i++) {
 4         Thread.sleep(10);
 5         final int finalI = i;
 6         executorService.execute(new Runnable() {
 7             @Override
 8             public void run() {
 9                 System.out.println(("线程名称:" + Thread.currentThread().getName() + ",执行" + finalI));
10             }
11         });
12     }
13 }

输出结果:

线程名称:pool-1-thread-1,执行1
线程名称:pool-1-thread-1,执行2
线程名称:pool-1-thread-1,执行3
线程名称:pool-1-thread-1,执行4
线程名称:pool-1-thread-1,执行5
线程名称:pool-1-thread-1,执行6
线程名称:pool-1-thread-1,执行7
线程名称:pool-1-thread-1,执行8
线程名称:pool-1-thread-1,执行9

第一次提交任务后,创建了pool-1-thread-1线程,执行完任务后有60s的空闲期,执行每一个任务时间非常短,所以只创建了一个线程且所有任务均由其执行。

2、FixedThreadPool线程池

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

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

测试示例:

1 public static void testNewFixedThreadPool() {
 2     ExecutorService executorService = Executors.newFixedThreadPool(3);
 3     for (int i = 0; i < 10; i++) {
 4         final int finalI = i;
 5         executorService.execute(new Runnable() {
 6             @Override
 7             public void run() {
 8                 try {
 9                     Thread.sleep(2000);
10                 } catch (InterruptedException e) {
11                     e.printStackTrace();
12                 }
13                 System.out.println(("线程名称:" + Thread.currentThread().getName() + ",执行" + finalI));
14             }
15         });
16     }
17 }

输出结果:

线程名称:pool-1-thread-3,执行2
线程名称:pool-1-thread-1,执行0
线程名称:pool-1-thread-2,执行1
线程名称:pool-1-thread-2,执行5
线程名称:pool-1-thread-1,执行4
线程名称:pool-1-thread-3,执行3
线程名称:pool-1-thread-3,执行8
线程名称:pool-1-thread-2,执行6
线程名称:pool-1-thread-1,执行7
线程名称:pool-1-thread-3,执行9

配置了3个核心线程,只有3个线程在执行任务。

3、SingleThreadExecutor线程池

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

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

测试示例:

1 public static void testNewSingleThreadExecutor() {
 2     ExecutorService executorService = Executors.newSingleThreadExecutor();
 3     for (int i = 1; i < 10; i++) {
 4         final int finalI = i;
 5         executorService.execute(new Runnable() {
 6             @Override
 7             public void run() {
 8                 try {
 9                     Thread.sleep(2000);
10                 } catch (InterruptedException e) {
11                     e.printStackTrace();
12                 }
13                 System.out.println(("线程名称:" + Thread.currentThread().getName() + ",执行" + finalI));
14             }
15         });
16     }
17 }

输出结果:

线程名称:pool-1-thread-1,执行1
线程名称:pool-1-thread-1,执行2
线程名称:pool-1-thread-1,执行3
线程名称:pool-1-thread-1,执行4
线程名称:pool-1-thread-1,执行5
线程名称:pool-1-thread-1,执行6
线程名称:pool-1-thread-1,执行7
线程名称:pool-1-thread-1,执行8
线程名称:pool-1-thread-1,执行9

尽管每个任务都执行时间都超过了2s,但始终只有1个线程在执行。

4、ScheduledThreadPool线程池

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

//ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

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

测试示例:

1 private static void testNewScheduledThreadPool() {
 2     ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
 3     executorService.schedule(new Runnable() {
 4         @Override
 5         public void run() {
 6             System.out.println("线程名称:" + Thread.currentThread().getName() + ",执行:3秒后执行");
 7         }
 8     }, 3, TimeUnit.SECONDS);
 9     executorService.scheduleAtFixedRate(new Runnable() {
10         @Override
11         public void run() {
12             System.out.println("线程名称:" + Thread.currentThread().getName() + ",执行:延迟2秒后每3秒执行一次");
13         }
14     }, 2, 3, TimeUnit.SECONDS);
15     for (int i = 0; i < 5; i++) {
16         final int finalI = i;
17         executorService.execute(new Runnable() {
18             @Override
19             public void run() {
20                 System.out.println("线程名称:" + Thread.currentThread().getName() + ",执行:普通任务-" + finalI);
21             }
22         });
23     }
24 }

输出结果:

线程名称:pool-1-thread-1,执行:普通任务-0
线程名称:pool-1-thread-2,执行:普通任务-1
线程名称:pool-1-thread-3,执行:普通任务-2
线程名称:pool-1-thread-4,执行:普通任务-3
线程名称:pool-1-thread-5,执行:普通任务-4
线程名称:pool-1-thread-2,执行:延迟2秒后每3秒执行一次
线程名称:pool-1-thread-1,执行:3秒后执行
线程名称:pool-1-thread-4,执行:延迟2秒后每3秒执行一次
线程名称:pool-1-thread-4,执行:延迟2秒后每3秒执行一次
线程名称:pool-1-thread-4,执行:延迟2秒后每3秒执行一次
线程名称:pool-1-thread-4,执行:延迟2秒后每3秒执行一次
线程名称:pool-1-thread-4,执行:延迟2秒后每3秒执行一次
线程名称:pool-1-thread-4,执行:延迟2秒后每3秒执行一次

定时和延迟执行。

九、线程池在AsyncTask中的使用

AsyncTask执行异步任务就是一个很典型的线程池使用范例,这里我们来看看其中是如何使用线程池的。

1、线程池参数的配置

1 private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
 2 // We want at least 2 threads and at most 4 threads in the core pool,
 3 // preferring to have 1 less than the CPU count to avoid saturating
 4 // the CPU with background work
 5 private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
 6 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
 7 private static final int KEEP_ALIVE_SECONDS = 30;
 8 private static final ThreadFactory sThreadFactory = new ThreadFactory() {
 9     private final AtomicInteger mCount = new AtomicInteger(1);
10     public Thread newThread(Runnable r) {
11         return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
12     }
13 };
14 private static final BlockingQueue<Runnable> sPoolWorkQueue =
15         new LinkedBlockingQueue<Runnable>(128);
16 
17 public static final Executor THREAD_POOL_EXECUTOR;
18 static {
19     ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
20             CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
21             sPoolWorkQueue, sThreadFactory);
22     threadPoolExecutor.allowCoreThreadTimeOut(true);
23     THREAD_POOL_EXECUTOR = threadPoolExecutor;
24 }
25 ......

2、AsyncTask执行任务使用范例

一般使用AsyncTask执行任务的时候,使用方式如下:

new AsyncTask<Object, Object, Object>() {
    @Override
    protected Object doInBackground(Object[] objects) {
        return null;
    }
}.execute();

3、AysncTask使用线程池执行任务源码分析

1 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
 2 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
 3 private static class SerialExecutor implements Executor {
 4     final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
 5     Runnable mActive;
 6     public synchronized void execute(final Runnable r) {
 7         mTasks.offer(new Runnable() {
 8             public void run() {
 9                 try {
10                     r.run();
11                 } finally {
12                     scheduleNext();
13                 }
14             }
15         });
16         if (mActive == null) {
17             scheduleNext();
18         }
19     }
20     protected synchronized void scheduleNext() {
21         if ((mActive = mTasks.poll()) != null) {
22             THREAD_POOL_EXECUTOR.execute(mActive);
23         }
24     }
25 }
26 
27 public final AsyncTask<Params, Progress, Result> execute(Params... params) {
28     return executeOnExecutor(sDefaultExecutor, params);
29 }
30 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
31     ......
32     exec.execute(mFuture);
33     return this;
34 }

第22行就是线程池调用了执行任务的方法。


喜欢本文的话,不妨顺手给我点个赞、评论区留言或者转发支持一下呗~