为什么
有了多线程,为什么还要用线程池?
- newFixedThreadPool
- newSingleThreadExecutor
- newCachedThreadPool
- newScheduledThreadPool
但是,《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,封装太好,让人少了思考。不过它里边还是利用 ThreadPoolExecutor 类实现的。
Executors 返回线程池对象的弊端如下: FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列⻓度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致 OOM。 CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
- 使用构造方法ThreadPoolExecutor 的方式创建,提倡这种方式,因为可以更清楚的理解线程池的运行规则。
ThreadPoolExecutor 类分析
ThreadPoolExecutor提供了四个构造方法,我们看的是参数最多的这个,其余3个都是在这个构造方法的基础上产生的(给定其他默认的构造参数)。public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}
参数分析
- corePoolSize:核心线程个数
- maximumPoolSize:最大线程个数
- workQueue:当新任务来的时候,如果线程池当前运行的线程个数已达到核心线程个数,那这个新任务会放到队列中。
- keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务 提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime 才会被回收销毁。
- unit:keepAliveTime 参数的时间单位。
- threadFactory:executor 创建新线程的时候会用到。
handler :拒绝策略。 ```java public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor(2,
5,10L,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
// 对象创建后,线程实际还没开始创建 // 执行execute时,检查当前池中线程数大小是否小于core number, 如果是,则创建新线程 threadPool.execute(()->{
System.out.println("任务1@" + Thread.currentThread().getName());sleepTime();System.out.println(1);
}); //检查当前池中线程数大小是否小于core number, 如果是,则创建新线程 threadPool.execute(() -> {
System.out.println("任务2@" + Thread.currentThread().getName()); sleepTime(); System.out.println(2);});
// 检查当前池中线程数大小是否小于core number, 如果不是,则偿试放入队列 // 这个任务是加到队列去的, 注意队列大小只有1, // 队列中的任务是什么时候取出来的? 任务1或者2结束后所占用的线程 会运行队列中的任务,这个任务是在最后才运行,比4运行的还晚 threadPool.execute(() -> {
System.out.println("任务3@" + Thread.currentThread().getName()); sleepTime(); System.out.println(3);});
// 检查当前池中线程数大小是否小于core number, 如果不是,则偿试放入队列,放入队列也失败,则增加新的worker线程
// 这个任务是加到core以外的新线程去的
threadPool.execute(() -> {
System.out.println("任务4@" + Thread.currentThread().getName());
sleepTime();
System.out.println(4);
});
}
拒绝策略
- ThreadPoolExecutor.AbortPolicy :抛出 RejectedExecutionException 来拒绝新任务的处理。
- ThreadPoolExecutor.CallerRunsPolicy :交给调用方来自行处理。
- ThreadPoolExecutor.DiscardPolicy : 不处理新任务,直接丢弃掉。
- ThreadPoolExecutor.DiscardOldestPolicy : 此策略将丢弃最早的未处理的任务请求。
在默认情况下, ThreadPoolExecutor的拒绝策略是AbortPolicy,抛出RejectedExecutionException 来拒绝新来的任务。
如何关闭线程池
方法就是这两个:shutdown()/shutdownNow()。
- shutdown() 执行后停止接受新任务,会把队列的任务执行完毕。
- shutdownNow() 也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。
