JUC(4)---java线程池原理及源码分析
线程池,既然是个池子里面肯定就装很多线程。
如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁 线程,如此一来会大大降低系统的效率。可能出现服务器在为每个请求创建新线程和销毁线 程上花费的时间和消耗的系统资源要比处理实际的用户请求的时间和资源更多。因此Java中提供线程池对线程进行统一的管理使用。
线程池可以让多个任务重用线程。减少线程创建,消亡的开销,提高性能。当任务达到不需要等待线程创建便可立即执行。提高线程的可管理性。使用线程池进行统一的分配,调优和监控。
Executor框架体系:
Executor接口是线程池框架中最顶级的接口,定义了一个用于执行Runnable的execute方法
体系简要类图:
ExecutorService也是一个重要的接口,其中也定义了一系列重要的方法:
- submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future 对象
- shutdown():在完成已提交的任务后封闭办事,不再接管新任务
- shutdownNow():停止所有正在履行的任务并封闭办事。
- isTerminated():测试是否所有任务都履行完毕了。
- isShutdown():测试是否该ExecutorService已被关闭。.
- invokeXXX(task,...):执行给定任务
从类图中可以看到一个很重要的实现类就是ThreadPoolExecutor,我们通过这个类看下线程池中比较重要的一些属性。
RUNNING:ctl高三位111,可以接受新任务,并且处理已经添加的任务。线程池的初始化状态就是running。
SHUTDOWN:ctl高三位000,不接受新任务,但是可以处理已经添加的任务。调用shutdown()方法,由running变成shutdown
STOP:ctl高三位001,不接受新任务,不处理已添加的任务,并且还会中断正在处理的任务。调用shutdownNow()方法,由running/shutdown变成stop
TIDYING:ctl高三位010,当所有的任务已终止,ctl记录的任务数量为0,线程池会变为tidying 状态。当线程池变为tidying 状态时,会执行钩子函数terminated()。terminated()在 ThreadPoolExecutor类中是空的,若用户想在线程池变为tidying 时,进行其他处理;可以通过重写terminated()。线程池为shutdown状态,并且阻塞队列是空的,并且执行的任务也是空就会由shutdown变成tidying;stop状态时,线程池中任务为空也会变成tidying
TERMINATED:ctl高三位011,线程池彻底凉凉了
当线程池处于tidying状态并且执行完了terminated()方法,就会由tidying变成terminated
构造方法:
corePoolSize:线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当 前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到 阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会 提前创建并启动所有核心线程。
maximumPoolSize:线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;
keepAliveTime:线程池维护线程所允许的空闲时间。当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime。
unit:keepAliveTime的单位。
workQueue:用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
2、LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,
3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
4、priorityBlockingQuene:具有优先级的无界阻塞队列;
threadFactory :ThreadFactory,用来创建新线程。默认使用 Executors.defaultThreadFactory() 来创建线程。使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。
Handler:线程池的拒绝策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
1、AbortPolicy:直接抛出异常,默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务;
3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务;
上面的4种策略都是ThreadPoolExecutor的内部类。 当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义策略。
通过execute或者submit向线程池提交任务。任务提交的时候先提交给核心线程(corePoolSize);如果核心线程满了,就将任务放到workQueue里面去排队等待;如果队列也满了(取决用的什么队列,以及设置的大小),就会将新进来的任务提交给非核心线程,非核心线程数量等于maximumPoolSize - corePoolSize,非核心线程使用之后会被回收。如果非核心线程也满了,那么就执行相应的拒绝策略RejectedExecutionHandler。

源码解析:
1.execute方法:
2. 可以看到关键方法是addWork方法,可以看到在addWork方法先会进行一系列判断,如果都通过了,才会进行任务的创建
3.Worker内部类,这类是继承了AbstractQueuedSynchronizer并且实现了Runable接口,其中还有两个重要属性一个是Thread, 一个是firstTask,初始化的时候会吧AQS中的state字段设置-1,后面允许中断会将这个值修改。
会通过线程工厂创建一个线程和当前的worker绑定,创建线程的runable接口对象就是work本身。worker重写的run方法实际调用了线程池的runWorker方法
4. 回到addWorker方法,创建完了worker,可以就可以获取到绑定的线程了,将worker添加到工作线程的集合中去。然后调用对象绑定线程的start方法,实际上会调用到worker的run方法,进而调用线程池的runWorker方法
5. runWorker 方法中会去worker中取任务,如果firstTask空,就去队列中取,因为之前在addwork的时候有些场景传入的firstWork是null。从队列取不到任务了,也就是getTask返回null了,结束while循环,调用processWorkerExit方法移除任务,处理最终的一些状态转换
就是说通过线程调用worker的run方法,然后借助worker里面再来调用我们传入线程池中的任务的run方法。所以说其实是起了一些线程,然后调用run方法一直尝试去取任务,取到之后手动调用的run方法执行任务。
JUC(4)---java线程池原理及源码分析的更多相关文章
- JAVA线程池原理与源码分析
1.线程池常用接口介绍 1.1.Executor public interface Executor { void execute(Runnable command); } 执行提交的Runnable ...
- Java ThreadPoolExecutor线程池原理及源码分析
一.源码分析(基于JDK1.6) ThreadExecutorPool是使用最多的线程池组件,了解它的原始资料最好是从从设计者(Doug Lea)的口中知道它的来龙去脉.在Jdk1.6中,Thread ...
- Java线程池ThreadPoolExector的源码分析
前言:线程是我们在学习java过程中非常重要的也是绕不开的一个知识点,它的重要程度可以说是java的核心之一,线程具有不可轻视的作用,对于我们提高程序的运行效率.压榨CPU处理能力.多条线路同时运行等 ...
- Java线程池ThreadPoolExecutor类源码分析
前面我们在java线程池ThreadPoolExecutor类使用详解中对ThreadPoolExector线程池类的使用进行了详细阐述,这篇文章我们对其具体的源码进行一下分析和总结: 首先我们看下T ...
- Java线程池及其底层源码实现分析
1.相关类 Executors ExecutorService Callable ThreadPool Future 2.相关接口 Executor Executor接口的使用: p ...
- 详解Java线程池的ctl(线程池控制状态)【源码分析】
0.综述 ctl 是线程池源码中常常用到的一个变量. 它的主要作用是记录线程池的生命周期状态和当前工作的线程数. 作者通过巧妙的设计,将一个整型变量按二进制位分成两部分,分别表示两个信息. 1.声明与 ...
- 线程池:ThreadPoolExecutor源码解读
目录 1 带着问题去阅读 1.1 线程池的线程复用原理 1.2 线程池如何管理线程 1.3 线程池配置的重要参数 1.4 shutdown()和shutdownNow()区别 1.5 线程池中的两个锁 ...
- java多线程系列(九)---ArrayBlockingQueue源码分析
java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...
- ConcurrentHashMap实现原理及源码分析
ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对Ha ...
随机推荐
- JavaScript正则表达式及jQuery回顾
JavaScript 正则表达式,用于规定在文本中检索的内容. 一.定义正则表达式: rep = /\d+/; // js定义正则.(python定义正则:re模块 rep = "\d+&q ...
- IDC:企业需求疲软 第三季度全球服务器市场收入下滑7%
根据IDC全球服务器季度追踪报告,2016年第三季度全球服务器市场同比减少7%至125亿美元.整个服务器市场的增长最近有所放缓,部分原因是超大规模数据中心增长放缓,以及受到高端服务器销售下滑的拖累.此 ...
- Linux shell基础(五)sed命令
一.sed命令 sed是一种强大的流式编辑器 (stream editor for filtering and transforming text),它能够完美的使用正则表达式,逐行处理文本并把结果显 ...
- System Call
内容 设计系统调用,将系统的相关信息(CPU型号.操作系统的版本号.系统中的进程等类似于Windows的任务管理器的信息)以文本形式列表显示于屏幕,并编写用户程序予以验证. 思想 系统调用是应用程序和 ...
- DAG的深度优先搜索标记
这是图论的基础知识点,也是学习Tarjan的导学课. 一.知识 对于在图G上进行深度优先搜索算法所产生的深度优先森林Gt,我们可以定义四种边的类型: 1.树边(Tree Edge):为深度优先森林中G ...
- P4430 小猴打架、P4981 父子
prufer编码 当然你也可以理解为 Cayley 公式,其实这个公式就是prufer编码经过一步就能推出的 P4430 小猴打架 P4981 父子 这俩题差不多 先说父子,很显然题目就是让你求\(n ...
- python sort和sorted的区别
sort 与 sorted 区别: sort 是应用在 list 上的方法,使用方法为 list.sort(),是对原有列表进行操作,改变原有列表的排序: sorted 可以对所有可迭代的对象进行排序 ...
- django源码分析——本地runserver分析
本文环境python3.5.2,django1.10.x系列 1.根据上一篇文章分析了,django-admin startproject与startapp的分析流程后,根据django的官方实例此时 ...
- 蒲公英 · JELLY技术周刊 Vol.05: Rust & Electron 的高性能实践 -- Finda
登高远眺 天高地迥,觉宇宙之无穷 基础技术 使用 JavaScript 框架的代价 作者从 JavaScript 下载时间.解析时间.执行时间.内存占用四个角度评测了 jQuery.Angular.R ...
- js 跳出循环
js 循环主要有 for while 主要有三种方式 :break continue return break是跳出当前整个循环语句,循环终止会继续执行该循环之后的代码 而continue是跳过当前循 ...