1、ThreadPoolExecutor的重要参数
1)corePoolSize核心线程数
核心线程一直存活,即使没有任务需要执行。当线程数小于核心线程数时,即使线程空闲,线程池也会优先创建新线程处理;
2)queueCapactiy任务队列容量(阻塞队列)
当核心线程数达到最大时,新任务会放在队列中排队等待执行;
3)maxPoolSize最大线程数
当前线程数>=corePoolSize,且任务队列已满,线程池会创建新线程处理任务;
当前线程数=maxPoolSize,且任务队列已经满,线程池会拒绝处理任务抛出异常;
4)keepAliveTime线程空闲时间
线程空闲时间达到keepAliveTime时,线程会退出,当线程数量大于corePoolSize,空闲时间达到keepAliveTime,就会shutdown;如果allowCoreThreadTimeout = true,则会直到线程数量为0;
如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
5)allowCoreThreadTimeout允许核心线程超时
6)workQueue :一个阻塞队列
用来存储等待执行的任务,当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。通过workQueue,线程池实现了阻塞功能;
1)有界任务队列ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
2)无界任务队列LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
3)直接提交队列synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
7)rejectExecutionHandle:任务拒绝处理器
两种情况:
线程数已达到maxPoolSize,并且队列已经满,会拒绝新任务;
线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果调用shutdown()线程真正提交任务,会拒绝新任务。
8)拒绝策略
AbortPolicy:丢弃任务并抛出RejectedExecutionException
CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
DiscardOldestPolicy:丢弃队列中最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
DiscardPolicy:丢弃任务,不做任何处理。
9)线程关闭
shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
2、执行顺序
workerCountOf()方法能够取得当前线程池中的线程的总数,取得当前线程数与核心池大小比较,
1) 当前线程数小于核心线程数,创建线程;通过addWorker()方法调度执行;addWorker(Runnable firstTask, boolean core)的主要任务是创建并启动线程。
2)线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列;
3)当线程数大于等于和核心程数,且任务队列已经满;
若线程数小于最大线程数,创建线程; 若线程等于最大线程数,执行拒绝策略,拒绝任务;
ThreadPoolExecutor
ExecutorService es = new ThreadPoolExecutor(1,1,60L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10));
addWorker共有四种传参方式。execute使用了其中三种,分别为:
1.addWorker(paramRunnable, true)
线程数小于corePoolSize时,放一个需要处理的task进Workers Set。如果Workers Set长度超过corePoolSize,就返回false.
2.addWorker(null, false)
放入一个空的task进workers Set,长度限制是maximumPoolSize。这样一个task为空的worker在线程执行的时候会去任务队列里拿任务,这样就相当于创建了一个新的线程,只是没有马上分配任务。
3.addWorker(paramRunnable, false)
当队列被放满时,就尝试将这个新来的task直接放入Workers Set,而此时Workers Set的长度限制是maximumPoolSize。如果线程池也满了的话就返回false.
3、不同线程池和应用场景
1)newCachedThreadPool
当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列。适用:执行很多短期异步的小程序或者负载较轻的服务器;
缓存线程池,默认存活60秒,阻塞队列使用的是SynchronousQueue是一个直接提交的阻塞队列;他总会迫使线程池增加新的线程去执行新的任务。在没有任务执行时,当线程的空闲时间超过keepAliveTime(60秒),则工作线程将会终止被回收,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。如果同时又大量任务被提交,而且任务执行的时间不是特别快,那么线程池便会新增出等量的线程池处理任务,这很可能会很快耗尽系统的资源。
2)newFixedThreadPool
创建可容纳固定数量线程的池子,每个线程的存活时间是无限的,当池子满了就不在添加线程了;适用:执行长期的任务,性能好很多;
固定大小的线程池,可以指定线程池的大小,该线程池corePoolSize和maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值。
任务提交十分频繁的时候,LinkedBlockingQueue会迅速增大,存在耗尽系统资源的问题。没有可运行线程,还是会占用系统资源,需要shutdown。
3)newSingleThreadExecutor
创建只一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)。适用:一个任务一个任务执行的场景;
阻塞队列为LinkedBlockingQueue,无界、基于链表、先进先出;
4)NewScheduledThreadPool
创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构。适用:周期性执行任务的场景;
定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。
scheduleAtFixedRate:是以固定的频率去执行任务,周期是指每次执行任务成功执行之间的间隔。
schedultWithFixedDelay:是以固定的延时去执行任务,延时是指上一次执行成功之后和下一次开始执行的之前的时间。