前言
要想用好线程池,就得掌握其原理,所谓磨刀不误砍柴工,深入了解线程池的工作原理,对日常工作开发,最重要的是应付面试。以前基本看了一一段时间就忘记了,究其根本还是没有理解性记忆,废话不说,我们来手写一个简化版的线程池,彻底掌握线程池的基本原理吧
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1)
try {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("测试");
}
};
// 队列有任务,返回任务,没有任务等待一段时间,依然没任务就返回null
workQueue.poll(11,TimeUnit.SECONDS);
// 队列有任务,返回任务,没有任务阻塞等待 知道队列当中有任务
workQueue.take();
// 往队列当中添加任务,成功添加返回true,返回false说明,队列满了
workQueue.offer(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
通过executor(task),添加一个任务,首先会判断当前线程数是否小于核心线程数,如果小于核心线程数,则创建核心线程并添加任务到该线程,如何超过了核心线程数,则添加到任务列队中;如果任务队里满了,就判断线程数是否小于最大线程数,如果小于,则创建非核心线程;如果不小于,则执行拒绝策略
// 核心线程池的数量
private int corePoolCount;
// 最大线程池的数量
private int maxPoolCount;
// 非核心线程的空闲后的存活时间
private int keepAliveTime;
// 非核心线程的空闲后的存活时间 单位 暂不实现,默认为秒s
// private int TimeUnit unit;
// 队列 线程安全
BlockingQueue<Runnable> workQueue;
// 线程工厂,暂时不实现
// ThreadFactory threadFactory;
// 拒绝策略,暂时不实现,我们采用抛异常的方式
//RejectedExecutionHandler handler;
// 构造方法初始化这些内部变量
public MyThreadPoolExecutor(int corePoolCount,
int maxPoolCount,
int keepAliveTime,
BlockingQueue<Runnable> workQueue) {
this.corePoolCount = corePoolCount;
this.maxPoolCount = maxPoolCount;
this.keepAliveTime = keepAliveTime;
this.workQueue = workQueue;
}
// 当前线程的状态
private AtomicInteger status = new AtomicInteger();
// 当前工作线程的数量
private AtomicInteger workerCount = new AtomicInteger();
// 当前工作线程
private HashSet<Worker> workers = new HashSet<>();
// 由于hashSet 线程不安全所以我们增加一个锁
private final Lock L =new ReentrantLock();
// 线程的状态有 总共有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED 本文先实现两种
// 启动状态
private final static Integer RUNNING = 0;
// 停止状态
private final static Integer STOP = 1;
// 获取当前完成的总任务数
private int finishedTaskCountTotal=0;
// 工作线程 内部类
private final class Worker implements Runnable{
private Thread thread;
private Runnable firstTask;
// 记录当前工作线程完成的任务情况
private int finishTaskCount=0;
public Worker(Runnable firstTask){
this.firstTask = firstTask;
this.thread = new Thread(this);
}
@Override
public void run() {
// 方法暂未实现,后面会实现 此处里一个flag1
runWorker(this);
}
}
public void execute(Runnable task) {
if(task==null){
throw new NullPointerException("别搞个空的过来");
}
if(status.get()==STOP) throw new RuntimeException("不能添加新任务了");
if(status.get() == RUNNING){
// 小于核心线程数量
if(workerCount.get()<corePoolCount&& addWorker(task,true)){
return;
}
// 核心线程满了,是不是就添加到工作队列里
// 队列本身线程安全
if(workQueue.offer(task)){
// 成功放入队列 返回
return;
}
// 到此处,说明可以添加非核心线程
if(workerCount.get()<maxPoolCount && addWorker(task,false)){
return;
}
// 创建不了执行拒绝策略
throw new RuntimeException("拒绝策略");
}
}
// 添加工作线程
private boolean addWorker(Runnable task, boolean core) {
if(status.get()==STOP){
return false;
}
retry:
while (true){
if(status.get()==STOP){
return false;
}
while (true){
// 想创建工作线程,但是已经 创建完了
if(workerCount.get()>=(core?corePoolCount:maxPoolCount)){
return false;
}
// 到这里,说明我们可以创建一个工作线程了
if(!casAddWorkerCount()){
// 自增失败,可能是因为高并发,导致的原子操作失败了
continue retry;
}
// 说明自增成功了,跳出双层循环
break retry;
}
}
Worker worker = null;
try {
L.lock();// 次数 操作workers 需要加锁,因为hashset线程不安全
worker =new Worker(task);
Thread thread = worker.thread;
if(thread!=null){
if(thread.isAlive()){
// 说明这个线程,不是我启动的,一般不会出现这种情况,源码的严谨
throw new IllegalStateException();
}
}
// start 方法的含义:开启一个新线程,在这个线程里面执行task的run方法 此处就会执行
// 上面的runWorker(this) 就是上面的flag1
thread.start();
workers.add(worker);
} catch (Exception e) {
e.printStackTrace();
}finally {
L.unlock();
}
return true;
}
private void runWorker(Worker worker) {
if(worker==null) throw new NullPointerException("空的工作线程");
try {
Runnable firstTask = worker.firstTask;
Thread wk = worker.thread;
worker.firstTask = null;
// 当前的任务先给他完成了
while(firstTask!=null || (firstTask=getTask())!=null){
// 如果执行线程池的stop方法,所有工作队里都要进行interrupted
if(wk.isInterrupted()){
System.out.println("this thread is interrupted");
}
if(status.get()==STOP){
System.out.println("this threadPoll has already stopped");
}
firstTask.run();
firstTask = null;
worker.finishTaskCount++;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 没任务了退出
while (true){
// 原子类的操作有可能失败,所以要不停重试,直到成功为止
if(casDeleteWorkerCount()){
finishedTaskCount += worker.finishTaskCount;
break;
}else{
continue;
}
}
L.lock();
try {
workers.remove(worker);
} finally {
L.unlock();
}
}
}
private Runnable getTask() {
Boolean timeOut = false;
try {
Runnable task = null;
while (true){
if(timeOut){
return null;
}
if(status.get()==STOP) return null;
// 工作线程小于等于核心线程数
if(workerCount.get()<=corePoolCount){
// 常驻
task = workQueue.take();
}else{
// 非核心线程
// 非常驻,等待一段时间,还没拿到就返回了
task = workQueue.poll(keepAliveTime, TimeUnit.SECONDS);
}
if(task!=null){
return task;
}
timeOut = true;
}
} catch (InterruptedException e) {
return null;
}
}
private boolean casAddWorkerCount() {
return workerCount.compareAndSet(workerCount.get(),workerCount.get()+1);
}
private boolean casDeleteWorkerCount() {
return workerCount.compareAndSet(workerCount.get(),workerCount.get()-1);
}
public void shutdown() {
L.lock();
try {
setState(STOP);
interruptAllWorkers();
} finally {
L.unlock();
}
}
private void interruptAllWorkers() {
L.lock();
try {
for (Worker worker : workers) {
if(!worker.thread.isInterrupted()){
worker.thread.interrupt();
}
this.finishedTaskCountTotal+=worker.finishTaskCount;
}
} finally {
L.unlock();
}
}
private void setState(Integer stop) {
if(status.get()==stop){
return;
}
while(true){
if(status.get()==stop){
break;
}
if(status.compareAndSet(status.get(),STOP)){
break;
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(1,5,200,new ArrayBlockingQueue<>(15));
for (int i = 0; i < 20; i++) {
final int s =i;
myThreadPoolExecutor.execute(()->{
System.out.println("打印"+s);
});
}
TimeUnit.SECONDS.sleep(2);
}
本文只是从一个java新手的角度,尝试梳理线程池的大致流程以及原理帮助自己梳理清楚线程池,还有很多未实现的,线程池的不同状态有不同的处理方式,还有shutdown和shutdownnow的区别,等等大家可以从源码当中找到答案。