Android线程池的简单使用

参考链接
https://blog.csdn.net/u012702547/article/details/52259529
https://blog.csdn.net/wolf909867753/article/details/77500625

普通线程的劣势

通常,在Android中使用线程的话,基本使用new Thread来创建线程
例如

new Thread(new Runnable() {  
            @Override  
            public void run() {  
                //耗时操作 
            }  
        }).start();  

但是在Java中,创建线程是消耗资源的,频繁的创建和销毁线程是相当消耗资源的,为此,引入了线程池的概念。

线程池的优势

使用线程池可以指定并发线程数,可以控制线程延时启动,可以设定线程阻塞超时(超时后从等待队列移除)等等,可以更好的控制线程,而不像new Thread一样,无法管控
一般情况下,任务的执行,主要时间消耗在如下几个部分
1.线程创建时间T1 2.任务等待线程执行时间T2 3.线程执行任务时间T3 4.线程销毁时间T4
通常,任务执行的时间取决于CPU的性能,所以T3相对可优化性不高,那么就智能优化T1 T2 T4了
我们可以通过合理创建线程数,减少不必要线程的创建销毁以减少T1 T4,合理维持线程空转(时刻等待任务,减少任务等待时间),以便有任务立刻执行,减小T2。后面我们会在实例中看到各种运用。

线程池的主要组成

线程池主要有以下四个部分,以下摘自本文

1.线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2.工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3.任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4.任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

Android中常见的线程池

  • ThreadPoolExecutor
    构造方法
    ThreadPoolExecutor构造方法有多个,只介绍参数较多的这个构造方法:
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        ...
    }

int corePoolSize:核心线程数,始终运行在线程池中的线程数,及时这些线程是空闲状态也不会销毁,除非指定了allowCoreThreadTimeOut参数

corePoolSize the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set

int maximumPoolSize:

maximumPoolSize the maximum number of threads to allow in the pool

线程池最大线程数
long keepAliveTime:

扫描二维码关注公众号,回复: 848779 查看本文章

keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle threads

非核心线程空闲的超时时间,如果非核心线程任务执行完毕,空闲状态超过keepAliveTime,则会被GC。如果allowCoreThreadTimeOut被指定,该参数也指核心线程超时时间
TimeUnit unit:

unit the time unit for the {@code keepAliveTime} argument

keepAliveTime的单位,指以下几种
NANOSECONDS
MICROSECONDS
MILLISECONDS
SECONDS
MINUTES
HOURS
DAYS

BlockingQueue workQueue:

workQueue the queue to use for holding tasks before they are executed.
This queue will hold only the {@code Runnable} tasks submitted by the
{@code execute} method.

工作队列,当线程数达到maximumPoolSize时,新创建的线程会被放到workQueue中等待其他线程执行完毕
corePoolSize maximumPoolSize workQueue三者数量关系:
corePoolSize为核心线程数
maximumPoolSize为核心线程+非核心线程数
workQueue为线程等待数
任务执行的优先级是这样的:如果有空闲的核心线程,交给核心线程,否则放到workQueue中,如果workQueue已满,则看看非核心线程是否满了,未满,放入非核心线程,否则抛出异常。
也就是:当创建的线程数>(maximumPoolSize+workQueue),则线程数量超出上限,抛出异常

ThreadFactory threadFactory:

threadFactory the factory to use when the executor creates a new
thread

用来创建线程的ThreadFactory类型。ThreadFactory是一个接口,通常需要指定实现类。常见实现类有以下两个
Executors.DefaultThreadFactory
Executors.PrivilegedThreadFactory
通常只要使用默认的ThreadFactory即可,除非自定义线程池才会使用到自定义ThreadFactory。自定义ThreadFactory需要指定线程优先级,name,守护状态,ThreadGroup等参数。

RejectedExecutionHandler handler:

handler the handler to use when execution is blocked because the
thread bounds and queue capacities are reached

当线程池中的线程数量已经达到最大数,会拒绝接受task,抛出异常。

特性
参数较多,基本可以定义自己想要的线程池,下面介绍的几种线程池大多数都可以通过指定ThreadPoolExecutor的参数来实现相同的效果
例子

    public static void main(String [] args){
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 5,  
                1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10));
        for (int i = 0; i < 15; i++) {  
            final int INDEX = i;  
            Runnable runnable = new Runnable() {

                public void run() {  
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("CHJ"+"run: " + INDEX);
                }  
            };  
            poolExecutor.execute(runnable);  
        }  
    }

创建线程池,核心线程3 非核心线程2(5-3),线程队列10(最大任务数15)

大家可能对LinkedBlockingDeque比较陌生,这其实是BlockingQueue的具体实现类。
BlockingQueue常见实现类:

ArrayBlockingQueue 实现基于数组,需指定大小,线程遵循FIFO(先入先出)
LinkedBlockingQueue 实现基于链表,可以不指定大小,此时队列大小为Integer.MAX_VALUE(也可指定大小),线程遵循FIFO(先入先出)
PriorityBlockingQueue 实现基于priority heap,无大小限制(逻辑上),添加线程操作可能由于OutOfMemoryError而失败,排序方式依赖Comparable(意味着队列中的任务是实现了Comparable的)
SynchronousQueue 异步队列,可以提高程序性能,但如果需要保持同步,不要使用
  • FixedThreadPool
    构造方法
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

可以看到,newFixedThreadPool指定了线程数,同时他只有核心线程。

线程的超时时间为0,说明核心线程即使在没有任务可执行的时候也不会被销毁(这样可让FixedThreadPool更快速的响应请求),最后的线程队列是一个LinkedBlockingQueue,但是LinkedBlockingQueue却没有参数,这说明线程队列的大小为Integer.MAX_VALUE(2的31次方减1),OK,看完参数,我们大概也就知道了FixedThreadPool的工作特点了,当所有的核心线程都在执行任务的时候,新的任务只能进入线程队列中进行等待,直到有线程被空闲出来。

特性
只有核心线程,且数目在初始化时就确定,workQueue不限大小。
例子

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 15; i++) {  
            final int INDEX = i;  
            Runnable runnable = new Runnable() {

                public void run() {  
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("CHJ"+"run: " + INDEX);
                }  
            };   
            fixedThreadPool.execute(runnable);
        }  

上述例子创建了一个创建了一个拥有3个核心线程,workQueue大小为Integer最大值的线程池,for循环创建了15个任务,线程池执行时先将3个task放入核心线程,其余放到workQueue,之后核心线程执行完毕后,从队列取出task继续执行。

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

可以看到,和FixedThreadPool极为类似,只不过SingleThreadExecutor只有一个核心线程
特性
只有一个核心线程
例子

public static void main(String [] args) throws InterruptedException{
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 15; i++) {  
            final int INDEX = i;  
            Runnable runnable = new Runnable() {

                public void run() {  
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("CHJ"+"run: " + INDEX);
                }  
            };     
            singleThreadPool.execute(runnable);
        }    
    }

上述例子创建了一个创建了一个拥有1个核心线程,workQueue大小为Integer最大值的线程池,for循环创建了15个任务,线程池执行时先将1个task放入核心线程,其余放到workQueue,之后核心线程执行完毕后,从队列取出task继续执行,每次只能执行一个任务。

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

可以看到,CachedThreadPool没有核心线程,有Integer.MAX_VALUE个非核心线程,非核心线程60s超时,同时workQueue是SynchronousQueue,也就是支持任务异步
特性
没有核心线程,非核心线程数为Integer.MAX_VALUE,支持task异步
例子

public static void main(String [] args) throws InterruptedException{
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 15; i++) {  
            final int INDEX = i;  
            Runnable runnable = new Runnable() {

                public void run() {  
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("CHJ"+"run: " + INDEX);
                }  
            };    
        }  
        cachedThreadPool.execute(runnable);
    }

上述例子创建了一个创建了一个拥有0个核心线程,非核心线程为Integer.MAX_VALUE,workQueue支持异步的线程池,for循环创建了15个任务,线程池执行时先将15个task放入workQueue,从队列取出task,使用非核心线程执行任务。

  • ScheduledThreadPool
    构造方法
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }    

可以看出,核心线程数可以指定,非核心线程数为Integer.MAX_VALUE,workQueue类型为DelayedWorkQueue
特性
可以指定任务完成后的等待时间
例子

    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService scheduledThreadPool = Executors
                .newScheduledThreadPool(2);
        Runnable runnable = null;
        runnable = new Runnable() {
            public void run() {
                System.out.println("Task start"
                        + format(System.currentTimeMillis()));
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("CHJ " + "run: " + " " + format(System.currentTimeMillis()) + "thread id"
                        + Thread.currentThread().getName());
                System.out.println("Task end" + format(System.currentTimeMillis()));
            }
        };
        System.out.println("Task start all"
                + format(System.currentTimeMillis()));
        // scheduledThreadPool.schedule(runnable, 1, TimeUnit.SECONDS);
        // scheduledThreadPool.scheduleAtFixedRate(runnable, 1, 4,
        // TimeUnit.SECONDS);
        scheduledThreadPool.scheduleWithFixedDelay(runnable, 1, 3,
                TimeUnit.SECONDS);
    }

    static String format(long time) {
        SimpleDateFormat formatter = new SimpleDateFormat("HH时mm分ss秒");
        Date date = new Date(time);
        return formatter.format(date);
    }
    scheduledThreadPool.schedule(runnable, 1, TimeUnit.SECONDS);
    延时1s启动线程
    scheduledThreadPool.scheduleAtFixedRate(runnable, 1, 4,
    TimeUnit.SECONDS);
    延时1s后启动线程,之后不管任务何时完成,从启动线程开始,每隔四秒尝试重新执行任务(如果任务没有执行完成,向后顺延直到任务完成)
    scheduledThreadPool.scheduleWithFixedDelay(runnable, 1, 3,
            TimeUnit.SECONDS);
    延时1s后启动线程,之后,任务完成后等待三秒,执行下一次任务

总结

FixedThreadPool 核心线程大小固定,线程等待队列大小为整型max,没有非核心线程
SingleThreadExecutor 与FixedThreadPool极为类似,只有一个核心线程
CachedThreadPool 没有核心线程,非核心线程数Integer.MAX_VALUE,支持task异步,线程空闲60s后自动回收
ScheduledThreadPool 可以指定任务执行频率,推迟时间等等,任务调度十分方便

猜你喜欢

转载自blog.csdn.net/u011109881/article/details/79936208