java 8 线程池_JAVA8线程池源码解析

1.对线程池的理解

1.1 艰辛摸索

看过许多关于线程池的介绍和讲解,看过方腾飞的 《并发编程的艺术》 也看过很多博客关于线程池的讲解,但是总觉得自己理解的不太好,总觉得哪里缺点。后来自己也花时间查看源码,自己去琢磨,多问为什么,结合一些博客的讲解。现在我想把这段时间的成果记录下来,也是为了加深自己的理解吧。

下面所有的源码都是java8 源码。

1.2 线程池的好处

降低性能消耗、提高响应速度:对于应用需要频繁创建线程,而且线程任务都比较简单,比如一些IO任务,线程的生命周期都很短;而线程的创建需要花费一定的CPU时间,所以当任务到来时如果线程已经准备就绪了,而不是重新创建,则会大大提高系统的响应速度。

对线程的集中管理监控:将创建的线程规约在线程池里,则可以对线程的数量和运行状态进行管理并进行监控,可对系统的线程资源进行集中管理。

2.线程池内的一些属性

2.1 线程池参数

看下面的这个线程池的构造器,他有许多参数,这些参数都代表什么意思?

corePoolSize 核心的线程池数量

maximumPoolSize 线程池内最大的线程数量

keepAliveTime 线程存活时间

unit 时间单位 比如 秒 分钟

workQueue 存储任务的阻塞队列

threadFactory 自定义线程工厂

handler 拒绝策略

3.Executors 提供的几种线程池

3.1 newFixedThreadPool

固定线程数的线程池

3.2 newCachedThreadPool

缓冲线程池

3.3 newWorkStealingPool

jdk 1.8 新加入的。创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不穿如并行级别参数,将默认为当前系统的CPU个数

3.4 newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

4. 源码

下面介绍下线程池的源码,由于对写作没有天赋,我就尽我最大努力把源码分析的浅显易懂一些。我介绍下线程池的一些状态属性,然后再从提交一个任务开始跟着源码进行描述一下我对源码的理解。

4.1 线程池状态 和 线程统计

format,png

image.png

ctl

先看看线程池中最重要的一个属性 ctl,我觉得是Control的简写,ctl控制着整个线程池的运转。ctl是AtomicInteger类型,线程池利用ctl 的高3位作为记录当前线程池的状态。利用低29位记录线程池中线程数,所以线程池中线程的最大容量为 2^29。

默认值是 1110 0000 0000 0000 0000 0000 0000 0000 = -536 870 912 ;

COUNT_BITS

COUNT_BITS = Integer.SIZE - 3;

COUNT_BITS 的意思是 一个整型数 有29位用于统计线程池内线程数;

RUNNING 运行状态

RUNNING 是线程池的初始状态,是一个int 类型的常量,值为 -1 左移 29 位即为 -536 870 912,线程池 的状态

只有RUNNING 是负数的。

SHUTDOWN

RUNNING --shutDowm()--> SHUTDOWN

RUNNING状态调用shutDowm()函数进入SHUTDOWN状态。是一个int类型的常量 值为0 ;

此时线程池不接收新任务,但能处理已添加的任务。

STOP

(RUNNING or SHUTDOWN) ---shutdownNow()--> STOP

RUNNING状态或者SHUTSOWN状态调用shutDowmNow()函数进入SROP状态。是一个int类型的常量 值为536 870 912 ;

不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。

TIDYING

是一个int类型的常量 值为1 073 741 824 ;

SHUTDOWN -> TIDYING:当线程池内线程数为0并且队列内任务数量为0时

STOP -> TIDYING:当线程池内线程数量为0时

TERMINATED

是一个int类型的常量 值为1 610 612 736 ;

线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

CAPACITY (线程的最大容量)

1 转移 29位 ,就是 0010 0000 0000 0000 0000 0000 0000 0000,

然后 减1 ,就变成了0001 1111 1111 1111 1111 1111 1111 1111,刚好就是 低29位 为1 ,就是线程内线程的最大容量 。

4.2 execute 执行任务

format,png

线程池添加任务流程图.png

在未来的某个时刻执行给定的任务,这个任务会被一个新线程或者一个已经存在的线程执行。

如果任务不能提交执行,那是因为线程池不处于运行状态或者已经到达容量上限。

那么这个任务就会被RejectedExecutionHandler处理。

execute()代码逻辑:

4.3 addWorker

检查是否可以根据当前线程池状态和绑定条件(核心线程或者最大线程)添加一个新的工作线程。

如果可以的话那么调整运行的线程数量。

并且 如果线程成功创建和运行的话,那么firstTask将是新线程的第一个任务被执行。

如果线程池已经关闭或者正在关闭,函数将返回false.

如果线程创建失败,可能的原因是因为线程工厂返回null,或者异常(最可能的异常就是在执行Thread,start()时产生OutOfMemoryErro )。

接着回滚以上操作。

参数:core:如果为true 增加核心线程,false 增加最大线程。

4.3 addWorkerFailed

执行addWorker添加任务失败之后 调用该方法执行回滚操作。

回滚操作主要做了以下三件事情:

4.4 runWorker 执行任务

添加worker 成功之后,worker就开始执行runWorker()了。

这个方法是比较重要的方法,是线程池的核心。是worker 线程 运行循环,重复地从队列中获取任务并执行它们,同时处理以下一些问题:

4.5 getTask() 获取任务

阻塞或者限时的通过getTask() 函数获取任务。不仅仅是获取任务,同时通过是不是返回null控制者worker 线程的退出与否。以下情况会导致函数返回null,导致worker 退出:

4.6 processWorkerExit 处理worker退出

对即将死亡worker 进行清理 和 记账(统计worker执行的任务数)

如果worker突然死亡(执行中有异常) 就需要调整运行的线程数

将worker 从worker线程集合中移除。(除名)

可能会终止线程池

在小于STOP状态前提下,如果由于执行 用户任务异常导致线程退出,创建新线程替代

在小于STOP状态前提下,不允许核心线程退出,如果运行中的线程小于核心线程,创建新线程替代。

4.7 tryTerminate 尝试终止池

1.如果池是shutdown 并且 阻塞队列 为空 并且 工作线程数 为空 转换为终止状态

2.如果 池是 stop 并且 工作线程为空 转换为 终止状态

3.满足终止条件,但是工作线程不为 0 ,终止一个空闲线程

4.8 interruptIdleWorkers

中断可能等待任务的线程。如果队列任务是空的,有的线程会被一直阻塞,调用该方法会中断那些被一直阻塞的线程:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值