Android线程之线程池

2018-05-24  本文已影响0人  bamboolmc

ThreadPoolExecutor(线程池)

构造方法

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

具体参数及其含义

TimeUnit.NANOSECONDS:纳秒
TimeUnit.MICROSECONDS:微秒
TimeUnit.MILLISECONDS:毫秒
TimeUnit.SECONDS:秒
TimeUnit.MINUTES:分
TimeUnit.HOURS:小时
TimeUnit.DAYS:天

调用策略

  1. 如果线程池中线程的数量小于核心线程数量,新任务来之后,开启新的核心线程去执行任务。
  2. 如果线程池中核心线程数量已经饱和,新任务来之后,则放到workQueue任务队列,代核心线程执行完任务后,从任务队列中取出待处理任务,继续处理。
  3. 如果线程池中核心线程数量已经饱和,workQueue任务队列中任务数也达到最大值,再来新任务,则开启一个非核心线程,进行处理任务。之后非核心线程和核心线程一同处理workQueue任务队列中任务。
  4. 如果线程池中核心线程数量饱和,workQueue任务队列中任务数也达到最大值,非核心线程也达到最大值,当再来到新任务后,那么线程池就调用RejectedExecutionHandler的rejectedExecution方法拒绝该任务。
  5. 特殊情况,如果线程池中无核心线程,并且任务数不大于任务队列限制,则线程池只开启一个非核心线程进行处理。

调用策略示例验证

  为了更好的理解调用策略,采用具体的示例,更清晰明了的进行理解。

package com.bamboolmc.threadpro;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {
    private static int taskSize = 2;
    private static int corePoolSize = 3;
    private static int maximumPoolSize = 4;
    private static int queueSize = 3;

    public static class TestTask implements Runnable{
        @Override
        public void run() {
            if (taskSize>0){
                try {
                    //模拟任务处理
                    Thread.sleep(500);
                    System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()
                            +" 完成一个任务,编号为t" + (taskSize--));

                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String args[]) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                1,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(queueSize));
        TestTask task;
        int size = taskSize;
        for (int i = 0; i < size; i++) {
            task = new TestTask();
            executor.execute(task);
        }
        executor.shutdown();

    }
}
对比调用策略1
    private static int taskSize = 2;
    private static int corePoolSize = 3;
    private static int maximumPoolSize = 4;
    private static int queueSize = 3;

任务数量2,核心线程数量3
运行结果:

1527001137241pool-1-thread-2 完成一个任务,编号为t1
1527001137241pool-1-thread-1 完成一个任务,编号为t2
对比调用策略2

修改如下

    private static int taskSize = 5;
    private static int corePoolSize = 3;
    private static int maximumPoolSize = 4;
    private static int queueSize = 3;

任务数量5,核心线程数量3,任务队列3
运行结果:

1527001567366pool-1-thread-2 完成一个任务,编号为t4
1527001567366pool-1-thread-1 完成一个任务,编号为t5
1527001567366pool-1-thread-3 完成一个任务,编号为t3
1527001567869pool-1-thread-1 完成一个任务,编号为t1
1527001567869pool-1-thread-2 完成一个任务,编号为t2
对比调用策略3

修改如下

    private static int taskSize = 7;
    private static int corePoolSize = 3;
    private static int maximumPoolSize = 4;
    private static int queueSize = 3;

任务数量7, 核心线程数量3,任务队列3,非核心线程1
运行结果:

1527002003014pool-1-thread-3 完成一个任务,编号为t6
1527002003014pool-1-thread-2 完成一个任务,编号为t4
1527002003014pool-1-thread-1 完成一个任务,编号为t7
1527002003014pool-1-thread-4 完成一个任务,编号为t5
1527002003516pool-1-thread-3 完成一个任务,编号为t3
1527002003516pool-1-thread-1 完成一个任务,编号为t2
1527002003516pool-1-thread-2 完成一个任务,编号为t1
对比调用策略4

修改如下

    private static int taskSize = 8;
    private static int corePoolSize = 3;
    private static int maximumPoolSize = 4;
    private static int queueSize = 3;

任务数量8, 核心线程数量3,任务队列3,非核心线程1
运行结果:
第8个任务运行时,线程池拒绝,抛出异常

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.bamboolmc.threadpro.ThreadPool$TestTask@14ae5a5 rejected from java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 4, active threads = 4, queued tasks = 3, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
    at com.bamboolmc.threadpro.ThreadPool.main(ThreadPool.java:46)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
1527002190243pool-1-thread-3 完成一个任务,编号为t8
1527002190243pool-1-thread-1 完成一个任务,编号为t6
1527002190243pool-1-thread-2 完成一个任务,编号为t5
1527002190243pool-1-thread-4 完成一个任务,编号为t7
1527002190749pool-1-thread-2 完成一个任务,编号为t3
1527002190749pool-1-thread-1 完成一个任务,编号为t3
1527002190749pool-1-thread-3 完成一个任务,编号为t4
对比调用策略5

修改如下

    private static int taskSize = 3;
    private static int corePoolSize = 0;
    private static int maximumPoolSize = 4;
    private static int queueSize = 3;

任务数量3, 核心线程数量0,任务队列3,非核心线程4
运行结果:

1527002486575pool-1-thread-1 完成一个任务,编号为t3
1527002487078pool-1-thread-1 完成一个任务,编号为t2
1527002487579pool-1-thread-1 完成一个任务,编号为t1

线程池分类

根据构建线程池时传入的参数不同,我们在Android系统中常见有4种不同功能特性的线程池。

我们可以用Executors.newXXX的方式去实例化我们需要的线程池,如实例化一个CachedThreadPool

 ExecutorService executorService =  Executors.newCachedThreadPool();

CachedThreadPool

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

核心线程数:0
最大线程数:2的31次方-1
超时时长:60秒
任务队列:SynchronousQueue
  由此可见,CachedThreadPool只有非核心线程,并且非核心线程最大数量可以任意大,当前已建线程都在执行任务时,再来新的任务会开启新的线程来执行,闲置线程超过60秒,就会被收回。由于使用了SynchronousQueue队列,所有提交的任务都会被立即执行。这类线程池比较适合大量的耗时较少的任务。

FixedThreadPool

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

核心线程数:固定(新建线程池时提供的参数)
最大线程数:同核心线程数
超时时长:0
任务队列:LinkedBlockingQueue
  由此可见,FixedThreadPool只有固定个数的核心线程数,无非核心线程,并且核心线程无超时限制,除非线程池关闭,否则线程即使处于空闲状态,也不会被回收,因此能够更快的响应外界请求。另外任务队列容量没有大小限制。当所有线程均有任务执行时,新任务会在任务队列排队,线程执行完,取出任务队列中任务执行。

SingleThreadExecutor

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

核心线程数:1
最大线程数:1
超时时长:0
任务队列:LinkedBlockingQueue
  由此可见,SingleThreadExecutor只有1个核心线程,所有任务都会在一个线程中按顺序执行;任务队列容量没有大小限制;另外如果某些错误而导致线程终止,则会创建新的线程继续执行后续的任务(与newFixedThreadPool(1)的不同,无法创建新的线程继续执行后续任务);由于是在一个线程执行任务,所以无需处理线程同步问题了。

ScheduledThreadPool

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

核心线程数:固定
最大线程数:2的31次方-1
超时时长:10毫秒
任务队列:DelayedWorkQueue
  由此可见,ScheduledThreadPool核心线程数固定,非核心线程数无限大,非核心线程超时时长10毫秒。主要用于执行定时任务和具有固定周期的任务。

使用方法异同

ExecutorService executor =  Executors.newCachedThreadPool();
executor.execute(new TestTask());
ExecutorService executor =  Executors.newFixedThreadPool(2);
executor.execute(new TestTask());
ExecutorService executor =  Executors.newSingleThreadExecutor();
executor.execute(new TestTask());

ScheduledExecutorService executor =  Executors.newScheduledThreadPool(2);
 executor.schedule(new TestTask(),2000,TimeUnit.MILLISECONDS);

任务队列BlockingQueue

  阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。

  四种类型线程池分析时,发现其中的任务队列参数分别使用到SynchronousQueue、LinkedBlockingQueue、DelayedWorkQueue这三种不同的任务队列。除此之外,系统还提供了PriorityBlockingQueue、ArrayBlockingQueue,DelayQueue,这些类均实现自BlockingQueue接口。

public interface BlockingQueue<E> extends Queue<E> {
    boolean add(E e);
    boolean offer(E e);
    void put(E e) throws InterruptedException;
    boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
    E take() throws InterruptedException;
    E poll(long timeout, TimeUnit unit) throws InterruptedException;
    int remainingCapacity();
    boolean remove(Object o);
    public boolean contains(Object o);
    int drainTo(Collection<? super E> c);
    int drainTo(Collection<? super E> c, int maxElements);
}

BlockingQueue作为一个接口,提供了添加、删除元素方法。

添加方法

删除方法

其他方法

SynchronousQueue

  没有元素存储空间的阻塞队列,如果在插入元素时后续没有执行取出的操作,那么插入的行为就会被阻塞。构造方法如下

public SynchronousQueue()
public SynchronousQueue(boolean fair)

LinkedBlockingQueue

  基于链表的阻塞队列,可指定容量,默认容量无穷大(2的31次方-1),采用FIFO(first-in-first-out)的访问策略。

public LinkedBlockingQueue()
public LinkedBlockingQueue(int capacity)
public LinkedBlockingQueue(Collection<? extends E> c)

PriorityBlockingQueue

  基于优先级的阻塞队列,队列容量由使用者指定,优先级的高低是如何指定的呢,可根据如下的构造函数中看到Comparator<? super E> comparator 就是指定优先级的比较器。因此队列元素(也就是要执行的任务)需要实现Comparator接口,来定义优先级高低。如果不设置容量大小的话,默认队列容量为11。

public PriorityBlockingQueue()
public PriorityBlockingQueue(int initialCapacity)
public PriorityBlockingQueue(int initialCapacity,Comparator<? super E> comparator)
public PriorityBlockingQueue(Collection<? extends E> c)

ArrayBlockingQueue

  基于数组的阻塞队列,队列容量由使用者指定,由于队列基于数组,初始化后,容量大小就是固定的。当设定boolean fair为true时,队列访问策略为FIFO;如果不设定,则默认false,访问策略是无序的。

public ArrayBlockingQueue(int capacity)
public ArrayBlockingQueue(int capacity, boolean fair)
public ArrayBlockingQueue(int capacity, boolean fair,Collection<? extends E> c)

其他

详细的阻塞队列可参考源码及如下文章学习
https://blog.csdn.net/qq_38989725/article/details/73298856

上一篇 下一篇

猜你喜欢

热点阅读