一、线程池的状态

RUNNING: 可以接收新任务;可以处理阻塞队列任务

SHUTDOWN: 不会接收新任务;但会处理阻塞队列剩余任务

STOP: 会中断正在执行的任务;并抛弃阻塞队列任务

TIDYING: 任务全部执行完毕,活动线程为0,即将进入终结

TERMINATED: 终结

二、ThreadPoolExecutor构造方法

ThreadPoolExecutor是jdk提供的线程池实现,先看一下全参数的构造方法

public ThreadPoolExecutor(int corePoolSize,//核心线程数
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//救急线程空闲时间
                              TimeUnit unit,//空闲时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列
                              ThreadFactory threadFactory,//线程工厂
                          RejectedExecutionHandler handler // 拒绝策略) {
    //构造方法
}

救急线程:核心线程全部繁忙任务队列也满了,就会启动救急线程来执行新添加到线程池的任务,当救急线程的空闲时间到达给定的时间时就会自动结束

救急线程数=最大线程数-核心线程数

任务队列:当线程池核心线程繁忙时存储提交到线程池的新任务

线程工厂: 给线程池指定创建线程的方式

拒绝策略:线程全部繁忙,任务队列也满时线程池怎样处理新添加的任务

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

示例

以下创建了一个线程,核心线程数是1,救急线程1,任务队列的容量是1,使用默认的线程工厂和拒绝策略

public class Test8 {

    static Logger LOG = LoggerFactory.getLogger(Test8.class);

    public static void main(String[] args) {
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(1,2,0, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>(1));

        executor.execute(new Runnable() {
            @Override
            public void run() {
                LOG.info("AAA");
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        executor.execute(new Runnable() {
            @Override
            public void run() {
                LOG.info("BBB");
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //这个任务线程池会创建救急线程执行,而B还在任务队列中等待,所以C会先执行
        executor.execute(new Runnable() {
            @Override
            public void run() {
                LOG.info("CCC");
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

三、Executors工具类中创建线程池的方法

3.1 newFixedThreadPool(int nThreads) 创建固定大小的线程池

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

从这个方法的源码可以看出这样创建的线程池的特点:

没有救急线程,任务队列是无界的(容量是Integer类型的最大值),任务较多时有可能造成内存溢出;使用的是默认的拒绝策略

3.2 newCachedThreadPool 带缓冲的线程池

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

这个线程池中的所有线程都是救急线程,救急线程的个数是Integer的最大值任务执行完后1min内没有新的任务就会结束运行。

SynchronousQueue 没有容量,没有线程来取是放不进去的,没有线程取put方法就会阻塞住。

这个线程池的特点是可以创建无限多的线程来执行任务,任务执行完后线程可以自动结束。

适合任务数比较密集,但每个任务执行时间较短的情况。

3.3 newSingleThreadExecutor 单线程线程池

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

救急线程数为0,核心线程是1

场景:希望多个任务排队执行,线程数固定为1其余任务会放入无界队列排队。

和自己创建一个线程来执行任务的区别:

自己创建的线程如果任务中发生异常线程就会结束而没有补救措施,其余任务就不能执行了;线程池中如果线程挂掉了会启动新的线程来保证池的正常工作,后续任务还可以继续执行。

和new newFixedThreadPool(1)的区别:

返回的对象不同,new newFixedThreadPool(1)对外暴露的是ThreadPoolExecutor,还可以通过set方法修改核心线程数,而newSingleThreadExecutor返回的对象不能被修改。

四、拒绝策略

当任务队列满了,核心线程和救急线程都繁忙时,线程会应用拒绝策略

4.1 AbortPolicy

抛出异常

4.2 CallerRunsPolicy

调用者自己来执行任务

4.3 DiscardPolicy

放弃新提交任务,什么都不做

4.4 DiscardOldestPolicy

丢弃任务队列中等待时间最长的一个任务,把当前任务提交进去

五、提交任务的方法

5.1 void execute

5.2 Future task)

5.3 List<Future> invokeAll(Collection<? extends Callable> tasks)

可以用来利用返回的Future对象等待多个任务执行完

5.4 invokeAny(Collection<? extends Callable> tasks)

可以用来利用返回的Future对象等待其中时间最短的任务执行完,其余的任务会被丢弃,包含任务队列中的任务和正在执行的任务(被中断)

六、关闭线程池的方法

6.1 shutdown

线程池状态变为SHUTDOWN,不会接收新任务,但已提交的任务(任务队列中的任务)会执行完,不会阻塞调用线程的执行。

6.2 shutdownNow

线程池状态变为STOP,不会接收新任务,会将队列中的任务返回,不再执行,用interrupt中断正在执行的任务