线程与线程池的那些事之线程池篇(万字长文)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"本文关键字:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"线程","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"线程池","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"单线程","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"多线程","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"线程池的好处","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"线程回收","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"创建方式","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"核心参数","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"底层机制","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"拒绝策略","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"参数设置","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"动态监控","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"线程隔离","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程和线程池相关的知识,是Java学习或者面试中一定会遇到的知识点,本篇我们会从线程和进程,并行与并发,单线程和多线程等,一直讲解到线程池,线程池的好处,创建方式,重要的核心参数,几个重要的方法,底层实现,拒绝策略,参数设置,动态调整,线程隔离等等。主要的大纲如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c5/c591ff5f6c93bc0dce66a8103082e5d7.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池的好处","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程池,使用了池化思想来管理线程,池化技术就是为了最大化效益,最小化用户风险,将资源统一放在一起管理的思想。这种思想在很多地方都有使用到,不仅仅是计算机,比如金融,企业管理,设备管理等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为什么要线程池?如果在并发的场景,编码人员根据需求来创建线程池,可能会有以下的问题:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们很难确定系统有多少线程在运行,如果使用就创建,不使用就销毁,那么创建和销毁线程的消耗也是比较大的","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假设来了很多请求,可能是爬虫,疯狂创建线程,可能把系统资源耗尽。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"实现线程池有什么好处呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"降低资源消耗:池化技术可以重复利用已经创建的线程,降低线程创建和销毁的损耗。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提高响应速度:利用已经存在的线程进行处理,少去了创建线程的时间","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"管理线程可控:线程是稀缺资源,不能无限创建,线程池可以做到统一分配和监控","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拓展其他功能:比如定时线程池,可以定时执行任务","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其实池化技术,用在比较多地方,比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"数据库连接池:数据库连接是稀缺资源,先创建好,提高响应速度,重复利用已有的连接","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"实例池:先创建好对象放到池子里面,循环利用,减少来回创建和销毁的消耗","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池相关的类","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面是与线程池相关的类的继承关系:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/91/91dbb496ba05a930cb4347729d88ddc9.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Executor","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Executor","attrs":{}}],"attrs":{}},{"type":"text","text":" 是顶级接口,里面只有一个方法","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"execute(Runnable command)","attrs":{}}],"attrs":{}},{"type":"text","text":",定义的是调度线程池来执行任务,它定义了线程池的基本规范,执行任务是它的天职。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"ExecutorService","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":" 继承了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Executor","attrs":{}}],"attrs":{}},{"type":"text","text":",但是它仍然是一个接口,它多了一些方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9a/9a6b75f7dddfe76eaa8288184dcb4067.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"void shutdown()","attrs":{}}],"attrs":{}},{"type":"text","text":":关闭线程池,会等待任务执行完。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"List shutdownNow()","attrs":{}}],"attrs":{}},{"type":"text","text":":立刻关闭线程池,尝试停止所有正在积极执行的任务,停止等待任务的处理,并","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"返回一个正在等待执行的任务列表(还没有执行的)","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"boolean isShutdown()","attrs":{}}],"attrs":{}},{"type":"text","text":":判断线程池是不是已经关闭,但是可能线程还在执行。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"boolean isTerminated()","attrs":{}}],"attrs":{}},{"type":"text","text":":在执行shutdown/shutdownNow之后,所有的任务已经完成,这个状态就是true。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"boolean awaitTermination(long timeout, TimeUnit unit)","attrs":{}}],"attrs":{}},{"type":"text","text":":执行shutdown之后,阻塞等到terminated状态,除非超时或者被打断。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":" Future submit(Callable task)","attrs":{}}],"attrs":{}},{"type":"text","text":": 提交一个有返回值的任务,并且返回该任务尚未有结果的Future,调用future.get()方法,可以返回任务完成的时候的结果。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":" Future submit(Runnable task, T result)","attrs":{}}],"attrs":{}},{"type":"text","text":":提交一个任务,传入返回结果,这个result没有什么作用,只是指定类型和一个返回的结果。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Future> submit(Runnable task)","attrs":{}}],"attrs":{}},{"type":"text","text":": 提交任务,返回Future","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":" List> invokeAll(Collection extends Callable> tasks)","attrs":{}}],"attrs":{}},{"type":"text","text":":批量执行tasks,获取Future的list,可以批量提交任务。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":" List> invokeAll(Collection extends Callable> tasks,long timeout, TimeUnit unit)","attrs":{}}],"attrs":{}},{"type":"text","text":":批量提交任务,并指定超时时间","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":" T invokeAny(Collection extends Callable> tasks)","attrs":{}}],"attrs":{}},{"type":"text","text":": 阻塞,获取第一个完成任务的结果值,","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":" T invokeAny(Collection extends Callable> tasks,long timeout, TimeUnit unit)","attrs":{}}],"attrs":{}},{"type":"text","text":":阻塞,获取第一个完成结果的值,指定超时时间","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可能有同学对前面的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":" Future submit(Runnable task, T result)","attrs":{}}],"attrs":{}},{"type":"text","text":"有疑问,这个reuslt有什么作用?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其实它没有什么作用,只是持有它,任务完成后,还是调用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"future.get()","attrs":{}}],"attrs":{}},{"type":"text","text":"返回这个结果,用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"result","attrs":{}}],"attrs":{}},{"type":"text","text":" new 了一个 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ftask","attrs":{}}],"attrs":{}},{"type":"text","text":",其内部其实是使用了Runnable的包装类 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"RunnableAdapter","attrs":{}}],"attrs":{}},{"type":"text","text":",没有对result做特殊的处理,调用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"call()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法的时候,直接返回这个结果。(Executors 中具体的实现)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public Future submit(Runnable task, T result) {\n if (task == null) throw new NullPointerException();\n RunnableFuture ftask = newTaskFor(task, result);\n execute(ftask);\n return ftask;\n }\n\n static final class RunnableAdapter implements Callable {\n final Runnable task;\n final T result;\n RunnableAdapter(Runnable task, T result) {\n this.task = task;\n this.result = result;\n }\n public T call() {\n task.run();\n // 返回传入的结果\n return result;\n }\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"还有一个方法值得一提:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"invokeAny()","attrs":{}}],"attrs":{}},{"type":"text","text":": 在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":"中使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":" 中的方法 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"invokeAny()","attrs":{}}],"attrs":{}},{"type":"text","text":" 取得第一个完成的任务的结果,当第一个任务执行完成后,会调用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"interrupt()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法将其他任务中断。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":"是接口,里面都是定义,并没有涉及实现,而前面的讲解都是基于它的名字(规定的规范)以及它的普遍实现来说的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":" 定义的是线程池的一些操作,包括关闭,判断是否关闭,是否停止,提交任务,批量提交任务等等。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"AbstractExecutorService","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"AbstractExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":" 是一个抽象类,实现了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":"接口,这是大部分线程池的基本实现,定时的线程池先不关注,主要的方法如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/90/90125abb1e14439db0cdd3e0634a3535.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不仅实现了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"submit","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"invokeAll","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"invokeAny","attrs":{}}],"attrs":{}},{"type":"text","text":" 等方法,而且提供了一个 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"newTaskFor","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法用于构建 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"RunnableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":" 对象,那些能够获取到任务返回结果的对象都是通过 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"newTaskFor","attrs":{}}],"attrs":{}},{"type":"text","text":" 来获取的。不展开里面所有的源码的介绍,仅以submit()方法为例:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public Future> submit(Runnable task) {\n if (task == null) throw new NullPointerException();\n // 封装任务\n RunnableFuture ftask = newTaskFor(task, null);\n // 执行任务\n execute(ftask);\n // 返回 RunnableFuture 对象\n return ftask;\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AbstractExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":" 是没有对最最重要的方法进行实现的,也就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"execute()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。线程池具体是怎么执行的,这个不同的线程池可以有不同的实现,一般都是继承 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AbstractExecutorService","attrs":{}}],"attrs":{}},{"type":"text","text":" (定时任务有其他的接口),我们最最常用的就是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/4482409d96ebfd2488372d3f1fba0587.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"重点来了!!!","attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":" 一般就是我们平时常用到的线程池类,所谓创建线程池,如果不是定时线程池,就是使用它。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":"的内部结构(属性):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ThreadPoolExecutor extends AbstractExecutorService {\n // 状态控制,主要用来控制线程池的状态,是核心的遍历,使用的是原子类\n private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));\n // 用来表示线程数量的位数(使用的是位运算,一部分表示线程的数量,一部分表示线程池的状态)\n // SIZE = 32 表示32位,那么COUNT_BITS就是29位\n private static final int COUNT_BITS = Integer.SIZE - 3;\n // 线程池的容量,也就是27位表示的最大值\n private static final int CAPACITY = (1 << COUNT_BITS) - 1;\n\n // 状态量,存储在高位,32位中的前3位\n // 111(第一位是符号位,1表示负数),线程池运行中\n private static final int RUNNING = -1 << COUNT_BITS; \n // 000\n private static final int SHUTDOWN = 0 << COUNT_BITS;\n // 001\n private static final int STOP = 1 << COUNT_BITS;\n // 010\n private static final int TIDYING = 2 << COUNT_BITS;\n // 011\n private static final int TERMINATED = 3 << COUNT_BITS;\n\n // 取出运行状态\n private static int runStateOf(int c) { return c & ~CAPACITY; }\n // 取出线程数量\n private static int workerCountOf(int c) { return c & CAPACITY; }\n // 用运行状态和线程数获取ctl\n private static int ctlOf(int rs, int wc) { return rs | wc; }\n \n // 任务等待队列\n private final BlockingQueue workQueue;\n // 可重入主锁(保证一些操作的线程安全)\n private final ReentrantLock mainLock = new ReentrantLock();\n // 线程的集合\n private final HashSet workers = new HashSet();\n \n // 在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),\n // 传统线程的通信方式,Condition都可以实现,Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition\n private final Condition termination = mainLock.newCondition();\n \n // 最大线程池大小\n private int largestPoolSize;\n // 完成的任务数量\n private long completedTaskCount;\n // 线程工厂\n private volatile ThreadFactory threadFactory;\n // 任务拒绝处理器\n private volatile RejectedExecutionHandler handler;\n // 非核心线程的存活时间\n private volatile long keepAliveTime;\n // 允许核心线程的超时时间\n private volatile boolean allowCoreThreadTimeOut;\n // 核心线程数\n private volatile int corePoolSize;\n // 工作线程最大容量\n private volatile int maximumPoolSize;\n // 默认的拒绝处理器(丢弃任务)\n private static final RejectedExecutionHandler defaultHandler =\n new AbortPolicy();\n // 运行时关闭许可\n private static final RuntimePermission shutdownPerm =\n new RuntimePermission(\"modifyThread\");\n // 上下文\n private final AccessControlContext acc;\n // 只有一个线程\n private static final boolean ONLY_ONE = true;\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"线程池状态","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从上面的代码可以看出,用一个32位的对象保存线程池的状态以及线程池的容量,高3位是线程池的状态,而剩下的29位,则是保存线程的数量:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" // 状态量,存储在高位,32位中的前3位\n // 111(第一位是符号位,1表示负数),线程池运行中\n private static final int RUNNING = -1 << COUNT_BITS; \n // 000\n private static final int SHUTDOWN = 0 << COUNT_BITS;\n // 001\n private static final int STOP = 1 << COUNT_BITS;\n // 010\n private static final int TIDYING = 2 << COUNT_BITS;\n // 011\n private static final int TERMINATED = 3 << COUNT_BITS;\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各种状态之间是不一样的,他们的状态之间变化如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8d/8d9dae20a9436627fe9465b0a9a43108.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RUNNING:运行状态,可以接受任务,也可以处理任务","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SHUTDOWN:不可以接受任务,但是可以处理任务","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"STOP:不可以接受任务,也不可以处理任务,中断当前任务","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TIDYING:所有线程停止","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TERMINATED:线程池的最后状态","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Worker 实现","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程池,肯定得有池子,并且是放线程的地方,在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":" 中表现为 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Worker","attrs":{}}],"attrs":{}},{"type":"text","text":",这是内部类:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/01/011a2f829b87221becdb7b9dd50f7c1a.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程池其实就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Worker","attrs":{}}],"attrs":{}},{"type":"text","text":" (打工人,不断的领取任务,完成任务)的集合,这里使用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HashSet","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private final HashSet workers = new HashSet();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Worker","attrs":{}}],"attrs":{}},{"type":"text","text":" 怎么实现的呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Worker","attrs":{}}],"attrs":{}},{"type":"text","text":" 除了继承了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AbstractQueuedSynchronizer","attrs":{}}],"attrs":{}},{"type":"text","text":",也就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AQS","attrs":{}}],"attrs":{}},{"type":"text","text":" , ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AQS","attrs":{}}],"attrs":{}},{"type":"text","text":" 本质上就是个队列锁,一个简单的互斥锁,一般是在中断或者修改 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"worker","attrs":{}}],"attrs":{}},{"type":"text","text":" 状态的时候使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内部引入","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AQS","attrs":{}}],"attrs":{}},{"type":"text","text":",是为了线程安全,线程执行任务的时候,调用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"runWorker(Worker w)","attrs":{}}],"attrs":{}},{"type":"text","text":",这个方法不是worker的方法,而是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":"的方法。从下面的代码可以看出,每次修改","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Worke","attrs":{}}],"attrs":{}},{"type":"text","text":"r的状态的时候,都是线程安全的。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Worker","attrs":{}}],"attrs":{}},{"type":"text","text":"里面,持有了一个线程","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Thread","attrs":{}}],"attrs":{}},{"type":"text","text":",可以理解为是对线程的封装。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至于","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"runWorker(Worker w)","attrs":{}}],"attrs":{}},{"type":"text","text":"是怎么运行的?先保持这个疑问,后面详细讲解。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" // 实现 Runnable,封装了线程\n private final class Worker\n extends AbstractQueuedSynchronizer\n implements Runnable\n {\n // 序列化id\n private static final long serialVersionUID = 6138294804551838833L;\n\n // worker运行的线程\n final Thread thread;\n \n // 初始化任务,有可能是空的,如果任务不为空的时候,其他进来的任务,可以直接运行,不在添加到任务队列\n Runnable firstTask;\n // 线程任务计数器\n volatile long completedTasks;\n\n // 指定一个任务让工人忙碌起来,这个任务可能是空的\n Worker(Runnable firstTask) {\n // 初始化AQS队列锁的状态\n setState(-1); // 禁止中断直到 runWorker\n this.firstTask = firstTask;\n // 从线程工厂,取出一个线程初始化\n this.thread = getThreadFactory().newThread(this);\n }\n\n // 实际上运行调用的是runWorker\n public void run() {\n // 不断循环获取任务进行执行\n runWorker(this);\n }\n\n // 0表示没有被锁\n // 1表示被锁的状态\n protected boolean isHeldExclusively() {\n return getState() != 0;\n }\n // 独占,尝试获取锁,如果成功返回true,失败返回false\n protected boolean tryAcquire(int unused) {\n // CAS 乐观锁\n if (compareAndSetState(0, 1)) {\n // 成功,当前线程独占锁\n setExclusiveOwnerThread(Thread.currentThread());\n return true;\n }\n return false;\n }\n // 独占方式,尝试释放锁\n protected boolean tryRelease(int unused) {\n setExclusiveOwnerThread(null);\n setState(0);\n return true;\n }\n // 上锁,调用的是AQS的方法\n public void lock() { acquire(1); }\n // 尝试上锁\n public boolean tryLock() { return tryAcquire(1); }\n // 解锁\n public void unlock() { release(1); }\n // 是否锁住\n public boolean isLocked() { return isHeldExclusively(); }\n\n // 如果开始可就中断\n void interruptIfStarted() {\n Thread t;\n if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {\n try {\n t.interrupt();\n } catch (SecurityException ignore) {\n }\n }\n }\n }\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"任务队列","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了放线程池的地方,要是任务很多,没有那么多线程,肯定需要一个地方放任务,充当缓冲作用,也就是任务队列,在代码中表现为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private final BlockingQueue workQueue;\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"拒绝策略和处理器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"计算机的内存总是有限的,我们不可能一直往队列里面增加内容,所以线程池为我们提供了选择,可以选择多种队列。同时当任务实在太多,占满了线程,并且把任务队列也占满的时候,我们需要做出一定的反应,那就是拒绝还是抛出错误,丢掉任务?丢掉哪些任务,这些都是可能需要定制的内容。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"如何创建线程池","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于如何创建线程池,其实 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadPoolExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":"提供了构造方法,主要参数如下,不传的话会使用默认的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"核心线程数:核心线程数,一般是指常驻的线程,没有任务的时候通常也不会销毁","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最大线程数:线程池允许创建的最大的线程数量","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非核心线程的存活时间:指的是没有任务的时候,非核心线程能够存活多久","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"时间的单位:存活时间的单位","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存放任务的队列:用来存放任务","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程工厂","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拒绝处理器:如果添加任务失败,将由该处理器处理","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" // 指定核心线程数,最大线程数,非核心线程没有任务的存活时间,时间单位,任务队列 \n public ThreadPoolExecutor(int corePoolSize,\n int maximumPoolSize,\n long keepAliveTime,\n TimeUnit unit,\n BlockingQueue workQueue) {\n this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,\n Executors.defaultThreadFactory(), defaultHandler);\n }\n // 指定核心线程数,最大线程数,非核心线程没有任务的存活时间,时间单位,任务队列,线程池工厂 \n public ThreadPoolExecutor(int corePoolSize,\n int maximumPoolSize,\n long keepAliveTime,\n TimeUnit unit,\n BlockingQueue workQueue,\n ThreadFactory threadFactory) {\n this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,\n threadFactory, defaultHandler);\n }\n // 指定核心线程数,最大线程数,非核心线程没有任务的存活时间,时间单位,任务队列,拒绝任务处理器\n public ThreadPoolExecutor(int corePoolSize,\n int maximumPoolSize,\n long keepAliveTime,\n TimeUnit unit,\n BlockingQueue workQueue,\n RejectedExecutionHandler handler) {\n this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,\n Executors.defaultThreadFactory(), handler);\n }\n // 最后其实都是调用了这个方法\n public ThreadPoolExecutor(int corePoolSize,\n int maximumPoolSize,\n long keepAliveTime,\n TimeUnit unit,\n BlockingQueue workQueue,\n ThreadFactory threadFactory,\n RejectedExecutionHandler handler) {\n ...\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其实,除了显示的指定上面的参数之外,JDK也封装了一些直接创建线程池的方法给我们,那就是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Executors","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" // 固定线程数量的线程池,无界的队列\n public static ExecutorService newFixedThreadPool(int nThreads) {\n return new ThreadPoolExecutor(nThreads, nThreads,\n 0L, TimeUnit.MILLISECONDS,\n new LinkedBlockingQueue());\n }\n // 单个线程的线程池,无界的队列,按照任务提交的顺序,串行执行 \n public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {\n return new FinalizableDelegatedExecutorService\n (new ThreadPoolExecutor(1, 1,\n 0L, TimeUnit.MILLISECONDS,\n new LinkedBlockingQueue(),\n threadFactory));\n }\n // 动态调节,没有核心线程,全部都是普通线程,每个线程存活60s,使用容量为1的阻塞队列\n public static ExecutorService newCachedThreadPool() {\n return new ThreadPoolExecutor(0, Integer.MAX_VALUE,\n 60L, TimeUnit.SECONDS,\n new SynchronousQueue());\n }\n // 定时任务线程池\n public static ScheduledExecutorService newSingleThreadScheduledExecutor() {\n return new DelegatedScheduledExecutorService\n (new ScheduledThreadPoolExecutor(1));\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是一般是不推荐使用上面别人封装的线程池的哈!!!","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池的底层参数以及核心方法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看完上面的创建参数大家可能会有点懵,但是没关系,一一为大家道来:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/eb/ebf5c72919a6fba8ad2e732040b7ef29.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看出,当有任务进来的时候,先判断核心线程池是不是已经满了,如果还没有,将会继续创建线程。注意,如果一个任务进来,创建线程执行,执行完成,线程空闲下来,这时候再来一个任务,是会继续使用之前的线程,还是重新创建一个线程来执行呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答案是重新创建线程,这样线程池可以快速达到核心线程数的规模大小,以便快速响应后面的任务。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果线程数量已经到达核心线程数,来了任务,线程池的线程又都不是空闲状态,那么就会判断队列是不是满的,倘若队列还有空间,那么就会把任务放进去队列中,等待线程领取执行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果任务队列已经满了,放不下任务,那么就会判断线程数是不是已经到最大线程数了,要是还没有到达,就会继续创建线程并执行任务,这个时候创建的是非核心部分线程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果已经到达最大线程数,那么就不能继续创建线程了,只能执行拒绝策略,默认的拒绝策略是丢弃任务,我们可以自定义拒绝策略。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"值得注意的是,倘若之前任务比较多,创建出了一些非核心线程,那么任务少了之后,领取不到任务,过了一定时间,非核心线程就会销毁,只剩下核心线程池的数量的线程。这个时间就是前面说的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"keepAliveTime","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"提交任务","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提交任务,我们看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"execute()","attrs":{}}],"attrs":{}},{"type":"text","text":",会先获取线程池的状态和个数,要是线程个数还没达到核心线程数,会直接添加线程,否则会放到任务队列,如果任务队列放不下,会继续增加线程,但是不是增加核心线程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public void execute(Runnable command) {\n if (command == null)\n throw new NullPointerException();\n // 获取状态和个数\n int c = ctl.get();\n // 如果个数小于核心线程数\n if (workerCountOf(c) < corePoolSize) {\n // 直接添加\n if (addWorker(command, true))\n return;\n // 添加失败则继续获取\n c = ctl.get();\n }\n // 判断线程池状态是不是运行中,任务放到队列中\n if (isRunning(c) && workQueue.offer(command)) {\n // 再次检查\n int recheck = ctl.get();\n // 判断线程池是不是还在运行\n if (! isRunning(recheck) && remove(command))\n // 如果不是,那么就拒绝并移除任务\n reject(command);\n else if (workerCountOf(recheck) == 0)\n // 如果线程数为0,并且还在运行,那么就直接添加\n addWorker(null, false);\n }else if (!addWorker(command, false))\n // 添加任务队列失败,拒绝\n reject(command);\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的源码中,调用了一个重要的方法:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"addWorker(Runnable firstTask, boolean core)","attrs":{}}],"attrs":{}},{"type":"text","text":",该方法主要是为了增加工作的线程,我们来看看它是如何执行的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" private boolean addWorker(Runnable firstTask, boolean core) {\n // 回到当前位置重试\n retry:\n for (;;) {\n // 获取状态\n int c = ctl.get();\n int rs = runStateOf(c);\n\n // 大于SHUTDOWN说明线程池已经停止\n // ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()) 表示三个条件至少有一个不满足\n // 不等于SHUTDOWN说明是大于shutdown\n // firstTask != null 任务不是空的\n // workQueue.isEmpty() 队列是空的\n if (rs >= SHUTDOWN &&\n ! (rs == SHUTDOWN &&\n firstTask == null &&\n ! workQueue.isEmpty()))\n return false;\n\n for (;;) {\n // 工作线程数\n int wc = workerCountOf(c);\n // 是否符合容量\n if (wc >= CAPACITY ||\n wc >= (core ? corePoolSize : maximumPoolSize))\n return false;\n // 添加成功,跳出循环\n if (compareAndIncrementWorkerCount(c))\n break retry;\n c = ctl.get(); // Re-read ctl\n // cas失败,重新尝试\n if (runStateOf(c) != rs)\n continue retry;\n // else CAS failed due to workerCount change; retry inner loop\n }\n }\n\n // 前面线程计数增加成功\n boolean workerStarted = false;\n boolean workerAdded = false;\n Worker w = null;\n try {\n // 创建了一个worker,包装了任务\n w = new Worker(firstTask);\n final Thread t = w.thread;\n // 线程创建成功\n if (t != null) {\n // 获取锁\n final ReentrantLock mainLock = this.mainLock;\n mainLock.lock();\n try {\n // 再次确认状态\n int rs = runStateOf(ctl.get());\n if (rs < SHUTDOWN ||\n (rs == SHUTDOWN && firstTask == null)) {\n // 如果线程已经启动,失败\n if (t.isAlive()) // precheck that t is startable\n throw new IllegalThreadStateException();\n // 新增线程到集合\n workers.add(w);\n // 获取大小\n int s = workers.size();\n // 判断最大线程池数量\n if (s > largestPoolSize)\n largestPoolSize = s;\n // 已经添加工作线程\n workerAdded = true;\n }\n } finally {\n // 解锁\n mainLock.unlock();\n }\n // 如果已经添加\n if (workerAdded) {\n // 启动线程\n t.start();\n workerStarted = true;\n }\n }\n } finally {\n // 如果没有启动\n if (! workerStarted)\n // 失败处理\n addWorkerFailed(w);\n }\n return workerStarted;\n }\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"处理任务","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面在介绍","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Worker","attrs":{}}],"attrs":{}},{"type":"text","text":"这个类的时候,我们讲解到其实它的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"run()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法调用的是外部的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"runWorker()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法,那么我们来看看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"runWorkder()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,它会直接处理自己的firstTask,这个任务并没有在任务队列里面,而是它自己持有的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final void runWorker(Worker w) {\n // 当前线程\n Thread wt = Thread.currentThread();\n // 第一个任务\n Runnable task = w.firstTask;\n // 重置为null\n w.firstTask = null;\n // 允许打断\n w.unlock();\n boolean completedAbruptly = true;\n try {\n // 任务不为空,或者获取的任务不为空\n while (task != null || (task = getTask()) != null) {\n // 加锁\n w.lock();\n //如果线程池停止,确保线程被中断;\n //如果不是,确保线程没有被中断。这\n //在第二种情况下需要复查处理\n // shutdown - now竞赛同时清除中断\n if ((runStateAtLeast(ctl.get(), STOP) ||\n (Thread.interrupted() &&\n runStateAtLeast(ctl.get(), STOP))) &&\n !wt.isInterrupted())\n wt.interrupt();\n try {\n // 执行之前回调方法(可以由我们自己实现)\n beforeExecute(wt, task);\n Throwable thrown = null;\n try {\n // 执行任务\n task.run();\n } catch (RuntimeException x) {\n thrown = x; throw x;\n } catch (Error x) {\n thrown = x; throw x;\n } catch (Throwable x) {\n thrown = x; throw new Error(x);\n } finally {\n // 执行之后回调方法\n afterExecute(task, thrown);\n }\n } finally {\n // 置为null\n task = null;\n // 更新完成任务\n w.completedTasks++;\n w.unlock();\n }\n }\n // 完成\n completedAbruptly = false;\n } finally {\n // 处理线程退出相关工作\n processWorkerExit(w, completedAbruptly);\n }\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面可以看到如果当前的任务是null,会去获取一个task,我们看看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getTask()","attrs":{}}],"attrs":{}},{"type":"text","text":",里面涉及到了两个参数,一个是是不是允许核心线程销毁,另外一个是线程数是不是大于核心线程数,如果满足条件,就从队列中取出任务,如果超时取不到,那就返回空,表示没有取到任务,没有取到任务,就不会执行前面的循环,就会触发线程销毁","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"processWorkerExit()","attrs":{}}],"attrs":{}},{"type":"text","text":"等工作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private Runnable getTask() {\n // 是否超时\n boolean timedOut = false; // Did the last poll() time out?\n\n for (;;) {\n int c = ctl.get();\n int rs = runStateOf(c);\n\n // SHUTDOWN状态继续处理队列中的任务,但是不接收新的任务\n if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {\n decrementWorkerCount();\n return null;\n }\n // 线程数\n int wc = workerCountOf(c);\n\n // 是否允许核心线程超时或者线程数大于核心线程数\n boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;\n\n if ((wc > maximumPoolSize || (timed && timedOut))\n && (wc > 1 || workQueue.isEmpty())) {\n // 减少线程成功,就返回null,后面由processWorkerExit()处理\n if (compareAndDecrementWorkerCount(c))\n return null;\n continue;\n }\n\n try {\n // 如果允许核心线程关闭,或者超过了核心线程,就可以在超时的时间内获取任务,或者直接取出任务\n Runnable r = timed ?\n workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :\n workQueue.take();\n // 如果能取到任务,那就肯定可以执行\n if (r != null)\n return r;\n // 否则就获取不到任务,超时了\n timedOut = true;\n } catch (InterruptedException retry) {\n timedOut = false;\n }\n }\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"销毁线程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面提到,如果线程当前任务为空,又允许核心线程销毁,或者线程超过了核心线程数,等待了一定时间,超时了却没有从任务队列获取到任务的话,就会跳出循环执行到后面的线程销毁(结束)程序。那销毁线程的时候怎么做呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" private void processWorkerExit(Worker w, boolean completedAbruptly) {\n // 如果是突然结束的线程,那么之前的线程数是没有调整的,这里需要调整\n if (completedAbruptly)\n decrementWorkerCount();\n // 获取锁\n final ReentrantLock mainLock = this.mainLock;\n mainLock.lock();\n \n try {\n // 完成的任务数\n completedTaskCount += w.completedTasks;\n // 移除线程\n workers.remove(w);\n } finally {\n // 解锁\n mainLock.unlock();\n }\n // 试图停止\n tryTerminate();\n // 获取状态\n int c = ctl.get();\n // 比stop小,至少是shutdown\n if (runStateLessThan(c, STOP)) {\n // 如果不是突然完成\n if (!completedAbruptly) {\n // 最小值要么是0,要么是核心线程数,要是允许核心线程超时销毁,那么就是0\n int min = allowCoreThreadTimeOut ? 0 : corePoolSize;\n // 如果最小的是0或者队列不是空的,那么保留一个线程\n if (min == 0 && ! workQueue.isEmpty())\n min = 1;\n // 只要大于等于最小的线程数,就结束当前线程\n if (workerCountOf(c) >= min)\n return; // replacement not needed\n }\n // 否则的话,可能还需要新增工作线程\n addWorker(null, false);\n }\n }\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"如何停止线程池","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"停止线程池可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"shutdown()","attrs":{}}],"attrs":{}},{"type":"text","text":"或者","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"shutdownNow()","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"shutdown()","attrs":{}}],"attrs":{}},{"type":"text","text":"可以继续处理队列中的任务,而","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"shutdownNow()","attrs":{}}],"attrs":{}},{"type":"text","text":"会立即清理任务,并返回未执行的任务。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public void shutdown() {\n // 获取锁\n final ReentrantLock mainLock = this.mainLock;\n mainLock.lock();\n try {\n // 检查停止权限\n checkShutdownAccess();\n // 更新状态\n advanceRunState(SHUTDOWN);\n // 中断所有线程\n interruptIdleWorkers();\n // 回调钩子\n onShutdown(); // hook for ScheduledThreadPoolExecutor\n } finally {\n mainLock.unlock();\n }\n tryTerminate();\n }\n // 立刻停止\n public List shutdownNow() {\n List tasks;\n // 获取锁\n final ReentrantLock mainLock = this.mainLock;\n mainLock.lock();\n try {\n // 检查停止权限\n checkShutdownAccess();\n // 更新状态到stop\n advanceRunState(STOP);\n // 中断所有线程\n interruptWorkers();\n // 清理队列\n tasks = drainQueue();\n } finally {\n mainLock.unlock();\n }\n tryTerminate();\n // 返回任务列表(未完成)\n return tasks;\n }\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"execute()和submit()方法","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"execute() ","attrs":{}}],"attrs":{}},{"type":"text","text":"方法可以提交不需要返回值的任务,无法判断任务是否被线程池执行是否成功","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"submit()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个对象,我们调用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"get()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法就可以","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"阻塞","attrs":{}},{"type":"text","text":",直到获取到线程执行完成的结果,同时我们也可以使用有超时时间的等待方法","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"get(long timeout,TimeUnit unit)","attrs":{}}],"attrs":{}},{"type":"text","text":",这样不管线程有没有执行完成,如果到时间,也不会阻塞,直接返回null。返回的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"RunnableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"对象,继承了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Runnable, Future","attrs":{}}],"attrs":{}},{"type":"text","text":"两个接口:","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface RunnableFuture extends Runnable, Future {\n /**\n * Sets this Future to the result of its computation\n * unless it has been cancelled.\n */\n void run();\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池为什么使用阻塞队列?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"阻塞队列,首先是一个队列,肯定具有先进先出的属性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而阻塞,则是这个模型的演化,一般队列,可以用在生产消费者模型,也就是数据共享,有人往里面放任务,有人不断的往里面取出任务,这是一个理想的状态。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是倘若不理想,产生任务和消费任务的速度不一样,要是任务放在队列里面比较多,消费比较慢,还可以慢慢消费,或者生产者得暂停一下产生任务(阻塞生产者线程)。可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"offer(E o, long timeout, TimeUnit unit)","attrs":{}}],"attrs":{}},{"type":"text","text":"设定等待的时间,如果在指定的时间内,还不能往队列中加入","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"BlockingQueue","attrs":{}}],"attrs":{}},{"type":"text","text":",则返回失败,也可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"put(Object)","attrs":{}}],"attrs":{}},{"type":"text","text":",将对象放到阻塞队列里面,如果没有空间,那么这个方法会阻塞到有空间才会放进去。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果消费速度快,生产者来不及生产,获取任务的时候,可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"poll(time)","attrs":{}}],"attrs":{}},{"type":"text","text":",有数据则直接取出来,没数据则可以等待","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"time","attrs":{}}],"attrs":{}},{"type":"text","text":"时间后,返回","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"null","attrs":{}}],"attrs":{}},{"type":"text","text":"。也可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"take()","attrs":{}}],"attrs":{}},{"type":"text","text":"取出第一个任务,没有任务就会一直阻塞到队列有任务为止。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面说了阻塞队列的属性,那么为啥要用呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果产生任务,来了就往队列里面放,资源很容易被耗尽。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"创建线程需要获取锁,这个一个线程池的全局锁,如果各个线程不断的获取锁,解锁,线程上下文切换之类的开销也比较大,不如在队列为空的时候,然一个线程阻塞等待。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"常见的阻塞队列","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0b/0b3e290a52869e3e8c1298cf7befe739.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ArrayBlockingQueue","attrs":{}},{"type":"text","text":":基于数组实现,内部有一个定长的数组,同时保存着队列头和尾部的位置。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"LinkedBlockingQueue","attrs":{}},{"type":"text","text":":基于链表的阻塞对垒,生产者和消费者使用独立的锁,并行能力强,如果不指定容量,默认是无效容量,容易系统内存耗尽。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"DelayQueue","attrs":{}},{"type":"text","text":":延迟队列,没有大小限制,生产数据不会被阻塞,消费数据会,只有指定的延迟时间到了,才能从队列中获取到该元素。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"PriorityBlockingQueue","attrs":{}},{"type":"text","text":":基于优先级的阻塞队列,按照优先级进行消费,内部控制同步的是公平锁。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SynchronousQueue","attrs":{}},{"type":"text","text":":没有缓冲,生产者直接把任务交给消费者,少了中间的缓存区。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池如何复用线程的?执行完成的线程怎么处理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面的源码分析,其实已经讲解过这个问题了,线程池的线程调用的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"run()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法,其实调用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"runWorker()","attrs":{}}],"attrs":{}},{"type":"text","text":",里面是死循环,除非获取不到任务,如果没有了任务firstTask并且从任务队列中获取不到任务,超时的时候,会再判断是不是可以销毁核心线程,或者超过了核心线程数,满足条件的时候,才会让当前的线程结束。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"否则,一直都在一个循环中,不会结束。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们知道","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"start()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法只能调用一次,因此调用到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"run()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法的时候,调用外面的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"runWorker()","attrs":{}}],"attrs":{}},{"type":"text","text":",让其在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"runWorker()","attrs":{}}],"attrs":{}},{"type":"text","text":"的时候,不断的循环,获取任务。获取到任务,调用任务的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"run()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"执行完成的线程会调用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"processWorkerExit()","attrs":{}}],"attrs":{}},{"type":"text","text":",前面有分析,里面会获取锁,把线程数减少,从工作线程从集合中移除,移除掉之后,会判断线程是不是太少了,如果是,会再加回来,个人以为是一种补救。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"如何配置线程池参数?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般而言,有个公式,如果是计算(CPU)密集型的任务,那么核心线程数设置为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"处理器核数-1","attrs":{}}],"attrs":{}},{"type":"text","text":",如果是io密集型(很多网络请求),那么就可以设置为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"2*处理器核数","attrs":{}}],"attrs":{}},{"type":"text","text":"。但是这并不是一个银弹,一切要从实际出发,最好就是在测试环境进行压测,实践出真知,并且很多时候一台机器不止一个线程池或者还会有其他的线程,因此参数不可设置得太过饱满。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般 8 核的机器,设置 10-12 个核心线程就差不多了,这一切必须按照业务具体值进行计算。设置过多的线程数,上下文切换,竞争激烈,设置过少,没有办法充分利用计算机的资源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"计算(CPU)密集型消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"io密集型系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"为什么不推荐默认的线程池创建方式?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"阿里的编程规范里面,不建议使用默认的方式来创建线程,是因为这样创建出来的线程很多时候参数都是默认的,可能创建者不太了解,很容易出问题,最好通过","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"new ThreadPoolExecutor()","attrs":{}}],"attrs":{}},{"type":"text","text":"来创建,方便控制参数。默认的方式创建的问题如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Executors.newFixedThreadPool():无界队列,内存可能被打爆","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Executors.newSingleThreadExecutor():单个线程,效率低,串行。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Executors.newCachedThreadPool():没有核心线程,最大线程数可能为无限大,内存可能还会爆掉。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用具体的参数创建线程池,开发者必须了解每个参数的作用,不会胡乱设置参数,减少内存溢出等问题。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般体现在几个问题:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"任务队列怎么设置?","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"核心线程多少个?","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最大线程数多少?","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"怎么拒绝任务?","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"创建线程的时候没有名称,追溯问题不好找。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池的拒绝策略","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程池一般有以下四种拒绝策略,其实我们可以从它的内部类看出来:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28f00a98a7f56844f2ff93a1640736b6.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AbortPolicy: 不执行新的任务,直接抛出异常,提示线程池已满","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DisCardPolicy:不执行新的任务,但是也不会抛出异常,默默的","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DisCardOldSetPolicy:丢弃消息队列中最老的任务,变成新进来的任务","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CallerRunsPolicy:直接调用当前的execute来执行任务","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般而言,上面的拒绝策略都不会特别理想,一般要是任务满了,首先需要做的就是看任务是不是必要的,如果非必要,非核心,可以考虑拒绝掉,并报错提醒,如果是必须的,必须把它保存起来,不管是使用mq消息,还是其他手段,不能丢任务。在这些过程中,日志是非常必要的。既要保护线程池,也要对业务负责。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池监控与动态调整","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程池提供了一些API,可以动态获取线程池的状态,并且还可以设置线程池的参数,以及状态:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查看线程池的状态:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/af7dedc627c677e378981cb613543582.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改线程池的状态:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/74/743a317802323ca86f8ab2105491ad66.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于这一点,美团的线程池文章讲得很清楚,甚至做了一个实时调整线程池参数的平台,可以进行跟踪监控,线程池活跃度、任务的执行Transaction(频率、耗时)、Reject异常、线程池内部统计信息等等。这里我就不展开了,原文:https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html ,这是我们可以参考的思路。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"线程池隔离","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程隔离,很多同学可能知道,就是不同的任务放在不同的线程里面运行,而线程池隔离,一般是按照业务类型来隔离,比如订单的处理线程放在一个线程池,会员相关的处理放在一个线程池。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也可以通过核心和非核心来隔离,核心处理流程放在一起,非核心放在一起,两个使用不一样的参数,不一样的拒绝策略,尽量保证多个线程池之间不影响,并且最大可能保住核心线程的运行,非核心线程可以忍受失败。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Hystrix","attrs":{}}],"attrs":{}},{"type":"text","text":"里面运用到这个技术,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Hystrix","attrs":{}}],"attrs":{}},{"type":"text","text":"的线程隔离技术,来防止不同的网络请求之间的雪崩,即使依赖的一个服务的线程池满了,也不会影响到应用程序的其他部分。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"关于作者","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"秦怀,公众号【","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"秦怀杂货店","attrs":{}},{"type":"text","text":"】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析,JDBC,Mybatis,Spring,redis,分布式,剑指Offer,LeetCode等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://aphysia.cn/archives/2020","title":"","type":null},"content":[{"type":"text","text":"2020年我写了什么?","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://damaer.github.io/Coding/#/","title":"","type":null},"content":[{"type":"text","text":"开源编程笔记","attrs":{}}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章