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 ...
随机推荐
- Linux系统介绍与环境搭建准备
1 什么是操作系统? 操作系统,Operating System,简称OS,是计算机系统中必不可少的基础软件,它是应用程序运行以及用户操作必备的基础环境支撑,是计算机系统的核心. 操作系统的作用是 ...
- OOM的起点到终点
前言 1.问题及现象 线上日志反馈内存溢出问题.根据用户反馈,客户操作一段时间之后,APP 内存溢出崩溃. 2.分析过程 (1) 分析线上日志,发现主要分两种: 第一种如下,可能是某个死循环导致内存不 ...
- mac OS vi/vim 使用教程
vi/vim 的使用 基本上 vi/vim 共分为三种模式 分别是 命令模式(Command mode) 输入模式(Insert mode) 底线命令模式(Last line mode) 命令模式: ...
- Eclipse Mac OS 安装中文简体语言包
打开Eclipse软件,在导航Eclipse下拉菜单中点开 About Eclipse 查看版本 我的是 Eclipse IDE for Enterprise Java Developers. Ver ...
- XmlSerializer .NET 序列化、反序列化
序列化对象 要序列化对象,首先创建要序列化的对象并设置其公共属性和字段.为此,您必须确定要将XML流存储的传输格式,作为流或文件. 例如,如果XML流必须以永久形式保存,则创建一个FileStre ...
- Android 项目 Android 学习手册(一)
前言: 当每次查询android 知识的时候,内心是凌乱的,总觉得要是有一个工具多好, 尤其在手机端如何可以查询的话,会非常完美,能大大减少选择查询的时间, 之前见了很多java 学习手册,把一些重要 ...
- 配置windows自动修改密码和自动登录
日常运维多台服务器,每季度要修改一次密码,非常麻烦,现做一个VBS脚本,计划任务设置每季度第一天三点定时运行,则可解决该问题,具体实现脚本如下:autochg_pwd.vbs ystr=year(No ...
- strip()的正则表达式版本
题目:写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样.如果只 传入了要去除的字符串,没有其他参数,那么就从该字符串首尾去除空白字符. 否则,函数第二个参数指定的字符将从该字符串 ...
- 网络流--最大流--EK模板
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...
- LCA 学习总结
怎么说,LCA裸题直接套板子,大家都会做,这样的题没必要看,剩下的题发先LCA只是一个工具就像是搜索一样,只是一个工具而不是一种算法,所以借助这套工具在其图论问题如最长路,数据结构等问题上再去发挥作用 ...