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 ...
随机推荐
- 截取nginx日志
截取nginx日志 sed -n '/24\/Feb\/2017:11:00:00/,/24\/Feb\/2017:12:00:00/p' yunying_api.wanglibao.com.acce ...
- QTP VBScript RegExp对象的运用
下面来讲讲RegExp对象: Vbs提供了针对正则表达式的一个非常实用的类,就是RegExp Global属性:代表全局匹配 IgnoreCase属性:大小写忽略 Pattern属性:正则表达式 Ex ...
- 通过express框架为前端提供api(数据),妈妈再也不用担心后端不给我数据了~
个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 首先,确保自己安装了express框架. 没有安装的同学可以参照下面这篇博 ...
- 瑞幸咖啡还是星巴克,一杯下午茶让我明白 设计模式--模板方法模式(Template Method Pattern)
简介 Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template M ...
- 数据库SQL语言从入门到精通--Part 3--SQL语言基础知识
数据库从入门到精通合集(超详细,学习数据库必看) 一.关系 单一的数据结构----关系 现实世界的实体以及实体间的各种联系均用关系来表示 逻辑结构----二维表 从用户角度,关系模型中数据的逻辑结构是 ...
- commons-logging slf4j log4j 区别
日志门面 1.Apache通用日志接口(commons-logging.jar) Apache Commons包中的一个,包含了日志功能,必须使用的jar包.这个包本身包含了一个Simple Logg ...
- springboot打包启动时报mybatis的typeAlias类名找不到的错误
springBoot项目在IDEA上面正常但是打包之后运行 出错,错误大致为mybatis解析Mapper.xml时 typeAlias找不到XX类 Mapper里面的标签: <select p ...
- 进程间通信之socketpair
socketpair是进程间通信的一种方式. API: ]); DEMO: #include <stdio.h> #include <stdlib.h> #include &l ...
- mysql优化–explain分析sql语句执行效率
Explain命令在解决数据库性能上是第一推荐使用命令,大部分的性能问题可以通过此命令来简单的解决,Explain可以用来查看SQL语句的执行效 果,可以帮助选择更好的索引和优化查询语句,写出更好的优 ...
- STM32 时钟树配置快速入门
layout: post tags: [STM32] comments: true 文章目录 layout: post tags: [STM32] comments: true 为什么要了解时钟树? ...