前言
线程池是Java面试必问问题之一!
有没有对源码滚瓜烂熟的童鞋?请举手! ♂️(怎么没人举手。。)
对了,今天先来撒一波狗狼~
(表打我~)
来,介绍下:
她叫码妞,是我码仔的女朋友喔!
她也在学习各类前端技术,可厉害了!
大家鼓掌欢迎吧!以后她会经常来问我问题的,要被烦了~
最近码妞也在看Java线程池呢,已经看得一头雾水了,正准备去问问码仔,
看码仔能不能给她讲明白了!
线程
线程是一种资源,并不是只存在程序的世界里。
程序,本来就是对生活的一种抽象表述。
比如像车站的售票窗口、退票窗口、检票窗口,每个窗口都在做不同的事情,就是车站里同时运行着的不同线程。
线程多了,需要管理,不同的线程也要能保证不会互相干扰,各做各的。
线程的生命周期
这个图很熟悉的吧~
好,开始讲线程池啦~
ThreadPoolExecutor
线程池源码里最主要的类了~
看下开头的这段注释:
看到英文就头晕?没事啦~
主要讲线程池重要的两个状态:
- runState:线程池运行状态
- workerCount:工作线程的数量
线程池用一个32位的int来同时保存runState和workerCount,其中高3位(第31到29位)是runState,其余29位是workerCount(大约500 million)。
来看看存储结构(码仔手动画的哦)
它的构造方法
- corePoolSize
核心线程数,好比班干部的人数。
- maximumPoolSize
最大线程数,好比教室里的座位数。 当提交任务数超过了这个最大值,线程还有拒绝策略——RejectExecutionHandler,做不动了嘛。
- keepAliveTime
除核心线程外的空闲线程保持存活时间。 当线程池里线程数超过corePoolSize数量了,keepAliveTime时间到,就把空闲线程关了,不然也闲置了呀,节省能量嘛。
- workQueue
任务阻塞队列。通过workQueue,线程池实现了阻塞功能。 当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。 - threadFactory
创建线程的工厂。所有的线程都是通过这个Factory创建的。
默认会使用
Executors.defaultThreadFactory() 来作线程工厂。 - handler 线程池的饱和策略。做不了任务了找理由罢工
- AbortPolicy
- 直接抛出异常,默认策略;
- CallerRunsPolicy
- 用调用者所在的线程来执行任务;
- DiscardOldestPolicy
- 丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy
- 直接丢弃任务。
Worker来了!
你看Worker的定义,其实它就是封装了的工作线程~
Worker既实现了Runnable,又继承了AbstractQueuedSynchronizer(AQS),所以它既是一个可执行的任务,又可以达到锁的效果。
看看Worker构造方法:
线程池是怎么工作的?
DuangDuangDuang!
看execute方法里的注释,一步步说得很清楚。
- 如果当前正在运行的线程数 < corePoolSize,尝试用给到的command来启动一个新线程作为第一个任务。
调用addWorker方法,检查runState和workerCount,并且如果增加线程的话,能防止产生错误警报,如果不能增加线程,则返回false。 - 如果一个任务被成功地加到队列里,仍然需要双重检验来确认是否需要新建一个线程。
(因为可能在上一次检查后,已经存在的线程已经died)或者进入这个方法后,线程池已经被关闭了。所以我们需要再次检查state,如果线程池停止了需要回滚入队列,如果池中没有线程了,新建一个线程。 - 如果不能把任务加入队列(可能线程池已经关闭或者满了),那么需要新开一个线程(往maxPoolSize发展)。如果失败的话,说明线程池shutdown了或者满了,就要拒绝这个任务了。
给你流程图!
工具类 Executors
线程池里还有个重要的类:Executors~
Executors是一个Java中的工具类,它提供工厂方法来创建不同类型的线程池。
用它可以很方便地创建出下面几种线程池来~
或者通过ThreadPoolExecutor的构造函数自定义需要的线程池。