Android技术知识Android开发Android开发

Android中的线程池

2018-08-27  本文已影响36人  大虾啊啊啊

前言

提到线程池,我们先说一下使用线程池的好处。使用线程池的优点可以概括为:
1、重复使用线程池中的线程,避免因为线程的创建和销毁带来的性能的开销而影响系统性能。
2、可以有效的控制线程的最大并发数,避免大量的线程之间因为互相抢占资源而导致堵塞现象。
3、对线程进行简单的管理,并提供定时执行以及循环间隔执行等功能。

一、ThreadPoolExecutor

在Android中的线程池来源于Java中的Executor,Executor是一个接口,真正的线程池实现类为Executor。下面我们来介绍一下ThreadPoolExecutor的创建方法。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

构造方法中有5个参数,接下来我们分别介绍这5个参数的含义:

corePoolSize:
线程池中核心的线程数,在默认的情况下核心线程会一直存活,即使是线程池中的线程处于闲置的状态。但是当ThreadPoolExecutor的allowCoreThreadTimeOut的属性设置为true的时候,即允许线程池中的核心线程被终止,通过keepAliveTime这个参数决定,这个参数意思就是线程池的闲置时长。当核心线程处于闲置状态并且超过了keepAliveTime超时时长,那么核心线程也会被终止(前提是allowCoreThreadTimeOut = true)
maximumPoolSize:
线程池中的最大线程数,当活动线程超过了最大线程数,那么后续的新任务就会处于阻塞状态。
keepAliveTime:
默认情况下指的是线程池中的非核心线程闲置的超时时长,当超过了这个时间,非核心线程就会被终止。当核心线程的allowCoreThreadTimeOut 属性为true的时候,核心线程闲置超过了该时长,也会被终止。
unit:
超时时长的单位
workQueue:
线程池中的任务队列,ThreadPoolExecutor中的execute方法传入的Runnable对象会存到盖队列中。
threadFactory:
线程工厂,为线程池创建新线程。
ThreadPoolExecutor执行任务的时候大致遵循以下原则,当有新任务要执行的时候。
(1)如果线程池中的核心线程未达到设置的核心线程数,那么就会开启一个核心线程执行新任务。
(2)如果线程池中的核心线程已经达到了设置的核心线程数,那么新的任务就会插入到线程池中的任务队列中等待被执行。
(3)如果(2)中的任务队列已满,并且线程池中的线程数量未达到设置的最大线程数,那么就会创建一个非核心线程去执行任务。
(4)如果(3)中线程池中的线程数已经达到了最大线程数,那么ThreadPoolExecutor就会调用RejectedExecutionHandler的rejectedExecution方法,通知调用者。

二、Android中常用的线程池分类

在Android中常用的线程池有四种:FixThreadPool、CachedThreadPool、ScheduleThreadPool、SingleThreadExecutor
(1)FixThreadPool(一堆人上公厕)
我们先看下FixThreadPool的初始化方法:

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

我们看到核心线程数和最大线程数是一样的。所以使用FixThreadPool的时候,核心线程是固定的,并且没有非核心线程,任务都在核心线程中执行,当任务数超过了核心线程数,那么新任务就会在任务队列中排队。
(2)CachedThreadPool(一堆人去一家很大很大的咖啡馆)

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

从源码中我们看到,没有核心线程,并且非核心线程为Integer.MAX_VALUE。这个值非常大,所以每次有新任务的时候,当线程池中没有闲置的线程,就会创建新的线程执行新任务。线程池中的任务队列为SynchronousQueue,该队列可以当做一个无法存储元素的队列。
(3)ScheduleThreadPool

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


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

ScheduleThreadPool的源码中可以看到,核心线程数是固定的,非核心线程不限制大小,当非核心线程闲置的时候,会被立即回收。任务队列我们看到是DelayedWorkQueue,从而看出,ScheduleThreadPool主要作用就是执行具有循环周期的定时任务。
(4)SingleThreadExecutor(公厕上只有一个坑位)
从英文单词上可以看出,就是单例的意思,我们来看下具体的实现:

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

从源码中可以看到,核心线程数和非核心线程数都是固定的,当线程池中的线程非闲置的情况下,新的任务要插入到任务队列中排队。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,使得任务之间不需要解决线程同步的问题。

小结

以上我们就简单的介绍了Android中的线程池的意义,初始化方法,以及在Android常用的线程池。

使用例子

 ivEtxt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "run: "+Thread.currentThread().getName() );
                ExecutorService fixedThreadPool =  Executors.newFixedThreadPool(10);
                fixedThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        Log.e(TAG, "run: "+Thread.currentThread().getName() );
                    }
                });
            }
        });

08-27 15:31:34.567 3055-3055/com.mujin.keji.myapplication E/MainActivity: run: main
08-27 15:31:34.568 3055-3079/com.mujin.keji.myapplication E/MainActivity: run: pool-2-thread-1

初始化FixedThreadPool后,调用execute方法,传入Runnable对象,在run()方法中执行的任务就是在线程池中的线程执行的。我们看打印结果就知道,线程池的名字是不一样的。

上一篇下一篇

猜你喜欢

热点阅读