通常在创建线程池时不指定此参数,它会使用默认的线程创建工厂的方法来创建线程,源代码如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { // Executors.defaultThreadFactory() 为默认的线程创建工厂 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } public static ThreadFactory defaultThreadFactory() { return new DefaultThreadFactory(); } // 默认的线程创建工厂,需要实现 ThreadFactory 接口 static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } // 创建线程 public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); // 创建一个非守护线程 if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); // 线程优先级设置为默认值 return t; } }
RejectedExecutionHandler
表示指定线程池的拒绝策略,当线程池的任务已经在缓存队列 workQueue 中存储满了之后,并且不能创建新的线程来执行此任务时,就会用到此拒绝策略.
它属于一种限流保护的机制,这里有四种任务拒绝类型:
AbortPolicy: 不执行新任务,直接抛出异常,提示线程池已满,涉及到该异常的任务也不会被执行,线程池默认的拒绝策略就是该策略。
DisCardPolicy: 不执行新任务,也不抛出异常,即忽略此任务;
DisCardOldSetPolicy: 将消息队列中的第一个任务(即等待时间最久的任务)替换为当前新进来的任务执行,忽略最早的任务(最先加入队列的任务);
CallerRunsPolicy: 把任务交给当前线程来执行;
/** * 线程池的拒绝策略 */ @Test public void test1() { // 创建线程池 核心线程为1,最大线程为3,任务队列大小为2 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(1, 3, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2), new ThreadPoolExecutor.AbortPolicy() // 添加 AbortPolicy 拒绝策略 ); for (int i = 0; i < 6; i++) { poolExecutor.execute(() -> { System.out.println(Thread.currentThread().getName()); }); } }自定义线程池拒绝策略
/** * 自定义线程池的拒绝策略 * 实现接口 RejectedExecutionHandler */ @Test public void test2() { ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { // 业务处理方法 System.out.println("执行自定义拒绝策略"); } } ); for (int i = 0; i < 6; i++) { executor.execute(() -> { System.out.println(Thread.currentThread().getName()); }); } } 线程池工作原理线程池的工作流程要从它的执行方法 execute() 说起,源码如下:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); // 当前工作的线程数小于核心线程数 if (workerCountOf(c) < corePoolSize) { // 创建新的线程执行此任务 if (addWorker(command, true)) return; c = ctl.get(); } // 检查线程池是否处于运行状态,如果是则把任务添加到队列 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); // 再出检查线程池是否处于运行状态,防止在第一次校验通过后线程池关闭 // 如果是非运行状态,则将刚加入队列的任务移除 if (! isRunning(recheck) && remove(command)) reject(command); // 如果线程池的线程数为 0 时(当 corePoolSize 设置为 0 时会发生) else if (workerCountOf(recheck) == 0) addWorker(null, false); // 新建线程执行任务 } // 核心线程都在忙且队列都已爆满,尝试新启动一个线程执行失败 else if (!addWorker(command, false)) // 执行拒绝策略 reject(command); }execute() VS submit()
execute() 和 submit() 都是用来执行线程池任务的,它们最主要的区别是,submit() 方法可以接收线程池执行的返回值,而 execute() 不能接收返回值。