java 一共含有四种线程池: newCachedThreadPool, newFixedThreadPool, newSingleThreadExecutor, newScheduledThreadPool。
newCachedThreadPool:顾名思义是一种可缓存的线程池, 线程池除了维护初始大小的线程外,当任务数量超出线程池大小时,便会新建线程, 而且当线程完成任务之后不会马上销毁,而是会保留一段时间(默认60s),这种极大的减少了线程创建和销毁的资源消耗, 当这种线程池的弊端是 线程最大值过大, 如果用于高并发且任务较长场景,很容易将内存全部吃光。 所以使用于执行短期异步小任务,或者并发量不高的场景。
newFixedThreadPool :是基于无界队列的线程池, 维护的线程数量是固定的,如果线程均在繁忙状态,则新任务会放入无界队列里。 适用于长期任务。 如果用于短期任务,任务数量 < 线程池数量时,性能不会跟newCachedThreadPool太大的区别,但是超出时, 因为是短期的, 所以任务会不断的被放入队列,又被取出, 时间间隔很短,并且过多的短期任务放入队列中,回使得内存吃紧,当任务数量过多时会造成很大的资源浪费。
newSingleThreadExecutor:顾名思义是只有一个单线程的线程池,具体场景不太清晰, 按照线程含义来看,适用于需要按顺序(FIFO, LIFO, 优先级)的执行任务的场景,以及thread confinement(变量只能由特定线程访问)的要求。
newScheduleThreadPool :如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构。这个线程的应用场景就很容易知道了, 需要周期性执行的任务使用该线程池。
线程池任务执行流程:
当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
备注:
一般如果线程池任务队列采用LinkedBlockingQueue队列的话,那么不会拒绝任何任务(因为队列大小没有限制),这种情况下,ThreadPoolExecutor最多仅会按照最小线程数来创建线程,也就是说线程池大小被忽略了。
如果线程池任务队列采用ArrayBlockingQueue队列的话,那么ThreadPoolExecutor将会采取一个非常负责的算法,比如假定线程池的最小线程数为4,最大为8所用的ArrayBlockingQueue最大为10。随着任务到达并被放到队列中,线程池中最多运行4个线程(即最小线程数)。即使队列完全填满,也就是说有10个处于等待状态的任务,ThreadPoolExecutor也只会利用4个线程。如果队列已满,而又有新任务进来,此时才会启动一个新线程,这里不会因为队列已满而拒接该任务,相反会启动一个新线程。新线程会运行队列中的第一个任务,为新来的任务腾出空间。
这个算法背后的理念是:该池大部分时间仅使用核心线程(4个),即使有适量的任务在队列中等待运行。这时线程池就可以用作节流阀。如果挤压的请求变得非常多,这时该池就会尝试运行更多的线程来清理;这时第二个节流阀—最大线程数就起作用了。
ThreadPoolExecutor:
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; } //因为ctl以(1<<29)开始递增,转化为以0开始递增的数字 例如 workerCOuntOf( (1<<29) +9 ) = 9;
private static int ctlOf(int rs, int wc) { return rs | wc; }