突然想看看线程池
- 在执行
Executor.execute(runnable)
或者submit(runnable/callable)
的时候,检查此时线程池中的线程数量是否达到核心线程数,如果还没有,则创建'核心线程'执行任务。(可以理解为在线程池中分为'核心线程'和'最大线程'两种种类的线程,'最大线程'在空闲一段时间之后就自己关闭,'核心线程'则会一直尝试获取工作)- 如果达到核心线程数,那么检查队列是否已满,如果没满,则将任务放入队列中等待消费。(在线程池中任务和线程不会直接交互,一般都会维护一个阻塞队列,任务来的时候尝试放入队列中,而线程则是统一从队列中拿取任务执行)
- 如果队列已满,那么检查线程数量是否达到最大线程数,如果没有的话则创建'最大线程'执行任务,否则的话则执行拒绝策略。
3.2 如何创建一个线程池#
我们先通过较底层的一个类ThreadPoolExecutor
来创建。
public class Test { /* -----为了便于理解,可以把线程池中的类型分成两类——[核心线程]和[最大线程]------ */ /* -----可以直接看main方法的例子,再上来看这里参数的注释方便理解------ */ /** * 核心线程数,线程池的核心线程到达这个数值之后接收任务便不再创建线程, * 而是放入队列等待消费,直到队列填满 */ private static final int CORE_POOL_SIZE = 1; /** * 最大线程数,当队列被填满时再接收新的任务的时候就会创建'最大线程'来缓解压力, * '最大线程'在空闲一段时间后会消亡,具体的空闲时间取决于下方的KEEP_ALIVE_TIME, * '最大线程'达到这个数值后便不再创建,举个例子,核心线程数为1,最大线程数为2, * 那么核心线程的数量最多为1,'最大线程'的数量最多为1(最大线程数-核心线程数) */ private static final int MAXIMUM_POOL_SIZE = 2; /** 最大线程的空闲时间,'最大线程'空闲时间达到这个数值时消亡,时间单位为下个参数TimeUnit*/ private static final int KEEP_ALIVE_TIME = 60; /** 空闲时间的计量单位*/ private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS; /** * 任务队列,为阻塞队列,阻塞队列的特点是 * 1.调用take()方法时,若队列为空则进入阻塞状态而不是返回空 * 2.调用put()方法时,若队列已满则进入阻塞状态 * 阻塞队列可以分为数组队列和链表队列(区别大概就是List和Linked的区别),可以通过设定 * 边界值的方式来决定队列中最多可以容纳多少任务,如果超出则创建最大线程或者采取拒绝策略 * 如果设定了边界值则为有界队列,否则则为无界队列(无界队列容易引起OOM,队列的大小应根据需求制定) */ private static final BlockingQueue<Runnable> BLOCKING_QUEUE = new ArrayBlockingQueue<>(1); /** 线程工厂,由该工厂产生执行任务的线程*/ private static final ThreadFactory THREAD_FACTORY = Executors.defaultThreadFactory(); /** * 拒绝策略,当已达到最大线程并且队列已满的时候对新来任务的处理措施,分为四种,由ThreadPoolExecutor内部类实现 * 1、ThreadPoolExecutor.CallerRunsPolicy 当前线程来执行其任务,也就是说调用executor.execute()的线程执行, 而不是线程池额外提供线程执行 * 2、ThreadPoolExecutor.AbortPolicy 直接抛出RejectedExecutionException异常。 * 3、ThreadPoolExecutor.DiscardPolicy 直接丢弃任务,不会对新来的任务进行任何处理,也不会得到任何反馈。 * 4、ThreadPoolExecutor.DiscardOldestPolicy 丢弃队列中最老的任务(指的是队列的第一个任务,调用poll()方法将其丢弃), 然后重新调用executor.execute()方法 */ private static final RejectedExecutionHandler REJECTED_EXECUTION_HANDLER = new ThreadPoolExecutor.AbortPolicy(); public static void main(String[] args) throws InterruptedException { // 创建一个主要线程数为1,最大线程数为2,队列大小为1的线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, TIME_UNIT, BLOCKING_QUEUE, THREAD_FACTORY, REJECTED_EXECUTION_HANDLER); // 此时线程池中没有任何线程,直接创建一个主要线程来执行 executor.execute(() -> { try { System.err.println("execute thread1:" + Thread.currentThread().getName()); // 睡眠1秒,验证下方的线程放入了队列中而不是再次创建线程 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } }); // 此时主要线程数已经达到最大,新来的任务放入队列中 executor.execute(() -> { try { System.err.println("execute thread2:" + Thread.currentThread().getName()); // 睡眠1秒 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } }); // 再次接收任务时,由于队列满了尝试创建最大线程数来执行 executor.execute(() -> { try { System.err.println("execute thread3:" + Thread.currentThread().getName()); // 睡眠1秒 TimeUnit.SECONDS.sleep(1); }