Java:多线程,线程池,ThreadPoolExecutor详解

1. ThreadPoolExecutor的一个经常使用的构造方法

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

参数说明:java

-corePoolSize       线程池中所保存的核心线程数。线程池启动后默认是空的,只有任务来临时才会建立线程以处理请求。prestartAllCoreThreads方法能够在线程池启动后即启动全部核心线程以等待任务。数组

-maximumPoolSize  线程池容许建立的最大线程数。当workQueue使用无界队列时(如:LinkedBlockingQueue),则此参数无效。less

-keepAliveTime      当前线程池线程总数大于核心线程数时,终止多余的空闲线程的时间。ide

-unit          keepAliveTime参数的时间单位。this

-workQueue       工做队列,若是当前线程池达到核心线程数时(corePoolSize),且当前全部线程都处于活动状态,则将新加入的任务放到此队列中。下面仅列几个经常使用的:spa

  • ArrayBlockingQueue:  基于数组结构的有界队列,此队列按FIFO原则对任务进行排序。若是队列满了还有任务进来,则调用拒绝策略。
  • LinkedBlockingQueue:  基于链表结构的无界队列,此队列按FIFO原则对任务进行排序。由于它是无界的,根本不会满,因此采用此队列后线程池将忽略拒绝策略(handler)参数;同时还将忽略最大线程数(maximumPoolSize)等参数
  • SynchronousQueue:   直接将任务提交给线程而不是将它加入到队列,实际上此队列是空的。每一个插入的操做必须等到另外一个调用移除的操做;若是新任务来了线程池没有任何可用线程处理的话,则调用拒绝策略。其实要是把maximumPoolSize设置成无界(Integer.MAX_VALUE)的,加上SynchronousQueue队列,就等同于Executors.newCachedThreadPool()。
  • PriorityBlockingQueue: 具备优先级的队列的有界队列,能够自定义优先级;默认是按天然排序,可能不少场合并不合适。

-handler          拒绝策略,当线程池与workQueue队列都满了的状况下,对新加任务采起的策略。线程

  • AbortPolicy:           拒绝任务,抛出RejectedExecutionException异常。默认值。
  • CallerRunsPolicy:   A handler for rejected tasks that runs the rejected task directly in the calling thread of the execute method, unless the executor has been shut down, in which case the task is discarded(没太弄懂意思,看不太懂,程序模拟半天也没得出啥结论。)
  • DiscardOldestPolicy:  若是执行程序还没有关闭,则位于工做队列头部的任务将被删除,而后重试执行程序(若是再次失败,则重复此过程)。这样的结果是最后加入的任务反而有可能被执行到,先前加入的都被抛弃了。
  • DiscardPolicy:      加不进的任务都被抛弃了,同时没有异常抛出

2. 详解及示范

  1. 一个任务进来(Runnable)时,若是核心线程数(corePoolSize未达到,则直接建立线程处理该任务;若是核心线程数已经达到则该任务进入工做队列(workQueue)。若是工做队列满了(只能是有界队列),则检查最大线程数(maximumPoolSize是否达到,若是没达到则建立线程处理任务(FIFO);若是最大线程数据也达到了,则调用拒绝策略(handler)。
  2. 若是workQueue使用LinkedBlockingQueue队列,由于它是无界的,队列永远不会满,因此maximumPoolSize参数是没有意义的,一样keepAliveTimeunithandler三个参数都无心义。
  3. 若是workQueue使用ArrayBlockingQueue队列,那么当心,由于此队列是有界的,必须当心处理拒绝策略。你看人家Executors类,压根就不使用ArrayBlockingQueue队列。
  4. 正常状况下,若是使用Executors静态工厂生成的几种经常使用线程池可以知足要求,建议就用它们吧。本身控制全部细节挺不容易的。 
package com.clzhang.sample.thread;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest3 {
    static class MyThread implements Runnable {
        private String name;

        public MyThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            // 作点事情
            try {
                Thread.sleep(1000);
                    
                System.out.println(name + " finished job!") ;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 建立线程池,为了更好的明白运行流程,增长了一些额外的代码
//        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(2);
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
//        BlockingQueue<Runnable> queue = new PriorityBlockingQueue<Runnable>();
//        BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();

// AbortPolicy/CallerRunsPolicy/DiscardOldestPolicy/DiscardPolicy ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy()); // 向线程池里面扔任务 for (int i = 0; i < 10; i++) { System.out.println("当前线程池大小[" + threadPool.getPoolSize() + "],当前队列大小[" + queue.size() + "]"); threadPool.execute(new MyThread("Thread" + i)); } // 关闭线程池 threadPool.shutdown(); } }

输出(采用LinkedBlockingQueue队列):rest

当前线程池大小[0],当前队列大小[0]
当前线程池大小[1],当前队列大小[0]
当前线程池大小[2],当前队列大小[0]
当前线程池大小[2],当前队列大小[1]
当前线程池大小[2],当前队列大小[2]
当前线程池大小[2],当前队列大小[3]
当前线程池大小[2],当前队列大小[4]
当前线程池大小[2],当前队列大小[5]
当前线程池大小[2],当前队列大小[6]
当前线程池大小[2],当前队列大小[7]
Thread1 finished job!
Thread0 finished job!
Thread3 finished job!
Thread2 finished job!
Thread4 finished job!
Thread5 finished job!
Thread6 finished job!
Thread7 finished job!
Thread8 finished job!
Thread9 finished job!
code

3. 回头看看Executors静态工厂方法生成线程池的源代码

Executors.newSingleThreadExecutor()blog

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

Executors.newFixedThreadPool()

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

Executors.newCachedThreadPool()

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

经过上述代码能够发现,用Executors静态工厂生成的几种经常使用线程池,均可以向里面插入n多任务:要么workQueue是无界的,要么maximumPoolSize是无界的。