08.14 玩转ThreadPoolExecutor及Future获取Excel数据

导读:创建线程池、Callable任务实现管理多任务读取Excel中数据,纯属个人学习总结及实践,不追求程序合理性或适不适用实际场景。

理论:

1、线程池之 ThreadPoolExecutor

1.1 创建

new ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); /<runnable> 参数最多的构造方法

参数说明:

int corePoolSize

核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

int maximumPoolSize,

线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。

long keepAliveTime,

表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

TimeUnit unit,

参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

TimeUnit.DAYS; //天

TimeUnit.HOURS; //小时

TimeUnit.MINUTES; //分钟

TimeUnit.SECONDS; //秒

TimeUnit.MILLISECONDS; //毫秒

TimeUnit.MICROSECONDS; //微妙

TimeUnit.NANOSECONDS; //纳秒

BlockingQueue<runnable> workQueue/<runnable>

一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

ArrayBlockingQueue; 基于数组的先进先出队列,此队列创建时必须指定大小;

LinkedBlockingQueue; 基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

SynchronousQueue; 这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。

ThreadFactory threadFactory

线程工厂,主要用来创建线程;

RejectedExecutionHandler handler

表示当拒绝处理任务时的策略,有以下四种取值:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

1.2 核心方法说明

  • execute()

这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。

  • submit()

是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现, 在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。

当创建线程池后,初始时,线程池处于RUNNING状态;

  • shutdown() 如果调用了这个方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
  • shutdownNow() 如果调用了这个方法,方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务,并且清空任务缓存 队列,返回尚未执行的任务

当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

1.3 描述

在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:

Executors.newCachedThreadPool(); //创建将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

Executors.newSingleThreadExecutor(); //创建将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;

Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池 创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用LinkedBlockingQueue;

2、Callable

位于java.util.concurrent包下,是个泛型接口,在它里面也只声明了一个方法:call(),其返回的类型就是传递进来的V类型,

怎么使用Callable呢?一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

Future submit(Callable task);

3、Future

是个接口,对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。

内部几个方法说明:

cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。

isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

isDone方法表示任务是否已经完成,若任务完成,则返回true;

get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回异常。

也就是说Future提供了三种功能:

1)判断任务是否完成;

2)能够中断任务;

3)能够获取任务执行结果。

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

4、FutureTask

RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

*********************************************************************** 黄金分割 ************************************************************************

实战:设置线程池,池中核心线程5个,最多10个线程(超过的线程入队里侯着),空闲线程最多存活500毫秒,读取本地excel文件(500多条数据),分多个任务去读取,每个任务读50条。开始放图:

玩转ThreadPoolExecutor及Future获取Excel数据

玩转ThreadPoolExecutor及Future获取Excel数据

玩转ThreadPoolExecutor及Future获取Excel数据

玩转ThreadPoolExecutor及Future获取Excel数据

玩转ThreadPoolExecutor及Future获取Excel数据

有不妥之处还请指正。

关于ThreadLocal:

玩转ThreadPoolExecutor及Future获取Excel数据

玩转ThreadPoolExecutor及Future获取Excel数据


分享到:


相關文章: