ThreadPoolExecutor是Java语言对于线程池的实现。池化技术是一种复用资源,减小开销的技术。线程是操做系统的资源,线程的建立与调度由操做系统负责,线程的建立与调度都要耗费大量的资源,其中线程建立须要占用必定的内存,而线程的调度须要不断的切换线程上下文形成必定的开销。同时线程执行完毕以后就会被操做系统回收,这样在高并发状况下就会形成系统频繁建立线程。java
为此线程池技术为了解决上述问题,使线程在使用完毕后不回收而是重复利用。若是线程可以复用,那么咱们就能够使用固定数量的线程来解决并发问题,这样一来不只节约了系统资源,并且也会减小线程上下文切换的开销。shell
ThreadPoolExecutor的构造函数有7个,它们分别是:并发
通常状况下,咱们只使用前五个参数,剩余两个咱们使用默认参数便可。函数
其实,线程池建立参数都与线程池的任务提交逻辑密切相关。根据源码描述能够得知:当提交一个新任务时(执行线程池的execute方法)会通过三个步骤的处理。高并发
corePoolSize
时,线程池会建立一个新的线程(建立新线程由传入参数threadFactory
完成)来处理任务,哪怕线程池中有空闲线程,依然会选择建立新线程来处理。corePoolSize
时,线程池会将新任务压入工做队列(参数中传递的workQueue
)等待调度。maximumPoolSize
。若是小于maximunPoolSize
则会新建线程来处理任务(这时咱们的keepAliveTime
参数就起做用了,它主要做用于这种状况下建立的线程,若是任务数量减少,这些线程闲置了,那么在超过keepAliveTime
时间后就会被回收)。若是大于了maximumPoolSize
就会交由任务拒绝处理器handler
处理。正如线程有不一样的状态同样,线程池也拥有不一样的运行状态。源码中提出,线程池有五种状态,分别为:测试
文章开头说到,线程在执行完毕以后会被操做系统回收销毁,那么线程池时如何保障线程不被销毁?首先看一个测试用例:操作系统
public static void testThreadState() { Thread thread = new Thread(() -> System.out.println("Hello world")); // 建立一个线程 System.out.println(thread.getState()); // 此时线程的状态为NEW thread.start(); // 启动线程,状态为RUNNING System.out.println(thread.getState()); try { thread.join(); System.out.println(thread.getState()); // 线程运行结束,状态为TERMINATED thread.start(); // 此时再启动线程会发生什么呢? } catch (InterruptedException e) { e.printStackTrace(); } }
结果输出:线程
NEW RUNNABLE Hello world TERMINATED Exception in thread "main" java.lang.IllegalThreadStateException at java.base/java.lang.Thread.start(Thread.java:794) at misc.ThreadPoolExecutorTest.testThreadState(ThreadPoolExecutorTest.java:90) at misc.ThreadPoolExecutorTest.main(ThreadPoolExecutorTest.java:114)
能够看出,当一个线程运行结束以后,咱们是不可能让线程起死回生从新启动的。既然如此ThreadPoolExecutor如何保障线程执行完一个任务不被销毁而继续执行下一个任务呢?code
其实这里就要讲到咱们最开始传入的参数workQueue
,它的接口类型为BlockingQueue<T>
,直译过来就是阻塞队列。这中队列有个特色,就是当队列为空而尝试出队操做时会阻塞。blog
基于阻塞队列的如上特色,ThreadPoolExecutor采用不断循环+阻塞队列的方式来实现线程不被销毁。
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { // 从工做队列中不断取任务。若是工做队列为空,那么程序会阻塞在这里 while (task != null || (task = getTask()) != null) { w.lock(); // 检查线程池状态 if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); try { //// 执行任务 //// task.run(); afterExecute(task, null); } catch (Throwable ex) { afterExecute(task, ex); throw ex; } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
想要关闭线程池能够经过调用shutdown()
和shutdownNow()
方法实现。两种方法有所不一样,其中调用shutdown()
方法会中止接收新的任务,处理工做队列中的任务,调用这个方法以后线程池会进入SHUTDOWN状态,此方法无返回值而且不抛出异常。
而shutdownNow()
方法会中止接收新的任务,并且会返回未完成的任务集合,同时这个方法也会抛出异常。
线程池建立有七个参数,这几个参数的相互做用能够建立出适应特定业务场景的线程池。其中最为重要的有三个参数分别为:corePoolSize
,maximumPoolSize
,workQueue
。其中前两个参数已经在上文中做了详细介绍,而workQueue
参数在线程池建立中也极为重要。workQueue
主要有三种:
经过上述三种队列的特性咱们能够得知,