网站首页 > 技术教程 正文
作者:iMononoke
博客:https://juejin.im/user/5c629a3051882562191755d8
前言
线程池是Java面试必问问题之一!
有没有对源码滚瓜烂熟的童鞋?请举手!?♂?(怎么没人举手。。)
对了,今天先来撒一波狗狼~ (表打我~)
来,介绍下:
她也在学习各类前端技术,可厉害了!
大家鼓掌欢迎吧!以后她会经常来问我问题的,要被烦了~
最近码妞也在看Java线程池呢,已经看得一头雾水了,正准备去问问码仔,
看码仔能不能给她讲明白了!
线程
线程是一种资源,并不是只存在程序的世界里。
程序,本来就是对生活的一种抽象表述。
比如像车站的售票窗口、退票窗口、检票窗口,每个窗口都在做不同的事情,就是车站里同时运行着的不同线程。
线程多了,需要管理,不同的线程也要能保证不会互相干扰,各做各的。
线程的生命周期
这个图很熟悉的吧~
好,开始讲线程池啦~
ThreadPoolExecutor
线程池源码里最主要的类了~
看下开头的这段注释:
/**
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* The runState provides the main lifecycle control, taking on values:
看到英文就头晕?没事啦~
主要讲线程池重要的两个状态:
runState:线程池运行状态
workerCount:工作线程的数量
@ReachabilitySensitive
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
线程池用一个32位的int来同时保存runState和workerCount,其中高3位(第31到29位)是runState,其余29位是workerCount(大约500 million)。
来看看存储结构(码仔手动画的哦)
它的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
}
corePoolSize
maximumPoolSize
keepAliveTime
workQueue
任务阻塞队列。通过workQueue,线程池实现了阻塞功能。 当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。
threadFactory
创建线程的工厂。所有的线程都是通过这个Factory创建的。
默认会使用
Executors.defaultThreadFactory 来作线程工厂。
handler 线程池的饱和策略。做不了任务了找理由罢工
AbortPolicy
直接抛出异常,默认策略;
CallerRunsPolicy
用调用者所在的线程来执行任务;
DiscardOldestPolicy
丢弃阻塞队列中靠最前的任务,并执行当前任务;
DiscardPolicy
直接丢弃任务。
核心线程数,好比班干部的人数。
最大线程数,好比教室里的座位数。 当提交任务数超过了这个最大值,线程还有拒绝策略——RejectExecutionHandler,做不动了嘛。
除核心线程外的空闲线程保持存活时间。 当线程池里线程数超过corePoolSize数量了,keepAliveTime时间到,就把空闲线程关了,不然也闲置了呀,节省能量嘛。
Worker来了!
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
Worker既实现了Runnable,又继承了AbstractQueuedSynchronizer(AQS),所以它既是一个可执行的任务,又可以达到锁的效果。
看看Worker构造方法:
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task ( if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory.newThread(this);
}
DuangDuangDuang!
public void execute(Runnable command) {
if (command == )
throw new PointerException;
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get;
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get;
}
/**
* 2、如果线程池RUNNING状态,且入队列成功
*/
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get;
//如果再次校验过程中,线程池不是RUNNING状态,
// 并且remove(command)--workQueue.remove成功,拒绝当前command
if (! isRunning(recheck) && remove(command))
reject(command);
//为什么只检查运行的worker数量是不是0呢?? 为什么不和corePoolSize比较呢??
// 只保证有一个worker线程可以从queue中获取任务执行就行了??
// 因为只要还有活动的worker线程,就可以消费workerQueue中的任务
else if (workerCountOf(recheck) == 0)
addWorker(, false);
}
/**
* 3、如果线程池不是running状态 或者 无法入队列
* 尝试开启新线程,扩容至maxPoolSize,
* 如果addWork(command, false) 失败了,拒绝当前command
*/
else if (!addWorker(command, false))
reject(command);
}}
看execute方法里的注释,一步步说得很清楚。
如果当前正在运行的线程数 < corePoolSize,尝试用给到的command来启动一个新线程作为第一个任务。调用addWorker方法,检查runState和workerCount,并且如果增加线程的话,能防止产生错误警报,如果不能增加线程,则返回false。
如果一个任务被成功地加到队列里,仍然需要双重检验来确认是否需要新建一个线程。
(因为可能在上一次检查后,已经存在的线程已经died)或者进入这个方法后,线程池已经被关闭了。所以我们需要再次检查state,如果线程池停止了需要回滚入队列,如果池中没有线程了,新建一个线程。
如果不能把任务加入队列(可能线程池已经关闭或者满了),那么需要新开一个线程(往maxPoolSize发展)。如果失败的话,说明线程池shutdown了或者满了,就要拒绝这个任务了。
给你流程图!
工具类 Executors
线程池里还有个重要的类:Executors~
Executors是一个Java中的工具类,它提供工厂方法来创建不同类型的线程池。
用它可以很方便地创建出下面几种线程池来~
ExecutorService singleService = Executors.newSingleThreadExecutor;
ExecutorService fixedService = Executors.newFixedThreadPool(9);
ExecutorService cacheService = Executors.newCacheThreadPool;
或者通过ThreadPoolExecutor的构造函数自定义需要的线程池。
近期文章:
8种减肥法,让你的APP瘦身30%
码仔技术周报--第一期
《每日一道面试题》 第三期
今日问题:
线程池的工作原理能不能徒手画出来啦?
快来码仔社群解锁新姿势吧!社群升级:Max你的学习效率
猜你喜欢
- 2024-10-30 读写锁,你难道不需要了解一下吗?
- 2024-10-30 谷歌云故障14个小时,系“队列突变大量积压”引起
- 2024-10-30 什么是AQS及其原理(aqs作用)
- 2024-10-30 面试官:谈谈这4种磁盘IO调度算法--CFQ、NOOP、Deadline、AS
- 2024-10-30 QT的信号槽机制简介(qt信号槽优缺点)
- 2024-10-30 Java AQS(AbstractQueuedSynchronizer)详解
- 2024-10-30 AQS是什么(AQS是什么药品)
- 2024-10-30 详解磁盘IO调度算法--CFQ、NOOP、Deadline、AS
- 2024-10-30 基于AbstractQueuedSynchronizer的并发类实现
- 2024-10-30 怎么理解Condition(怎么理解金生水)
你 发表评论:
欢迎- 最近发表
-
- linux CentOS检查见后门程序的shell
- 网络安全工程师演示:黑客是如何使用Nmap网络扫描工具的?
- Linux中ftp服务修改默认21端口等(linux修改ftp配置文件)
- Linux系统下使用Iptables配置端口转发,运维实战收藏!
- 谈谈TCP和UDP源端口的确定(tcp和udp的端口号相同吗)
- Linux 系统 通过端口号找到对应的服务及相应安装位置
- 快速查找NAS未占用端口!Docker端口秒级排查+可视化占坑双杀技
- 【知识杂谈#2】如何查看Linux的(本地与公网)IP地址与SSH端口号
- 如何在Linux中查询 DNS 记录,这三个命令可谓是最常用、最经典的
- 【Linux系统编程】特殊进程之守护进程
- 标签列表
-
- 下划线是什么 (87)
- 精美网站 (58)
- qq登录界面 (90)
- nginx 命令 (82)
- nginx .http (73)
- nginx lua (70)
- nginx 重定向 (68)
- Nginx超时 (65)
- nginx 监控 (57)
- odbc (59)
- rar密码破解工具 (62)
- annotation (71)
- 红黑树 (57)
- 智力题 (62)
- php空间申请 (61)
- 按键精灵 注册码 (69)
- 软件测试报告 (59)
- ntcreatefile (64)
- 闪动文字 (56)
- guid (66)
- abap (63)
- mpeg 2 (65)
- column (63)
- dreamweaver教程 (57)
- excel行列转换 (56)
本文暂时没有评论,来添加一个吧(●'◡'●)