也来分析下Java 线程池(ThreadPoolExecutor)的原理

来源:互联网 发布:云服务器ecs绑定域名 编辑:程序博客网 时间:2024/06/06 18:55

先提几个问题:
1.线程池的工作原理是啥?
2.核心线程池是一开始时就创建够吗?空闲时核心线程池也一直维持不变吗?
3.线程池是如何被停止的?

如果你都能非常自信地回答出来,那后面的内容可以不用看了。


参考文章:
《Java 线程池(ThreadPoolExecutor)原理分析与使用》

一、线程池的使用

1.创建线程池:

通过new创建,构造函数为:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

2.提交任务:

public void execute(Runnable command)

3.关闭线程池:

1.public void shutdown()
2.public List shutdownNow()
两个方法的区别:
shutdown方法是尝试将所有空闲线程intrupt掉;而shutdownNow是将所有活动中的线程以及空闲线程都intrupt掉。

二、实现原理

这里写图片描述

三、原理分析

1.用来充当线程池的是一个数组:private final HashSet workers;
2.每个提交的任务都是一个Runnable,进入线程池后,会被包装成为一个 Worker;
3.Worker类是个内部类,自身实现了Runnable接口。它有两个成员变量:

Thread thread,Runnable firstTask,Worker(Runnable firstTask) {    setState(-1); // inhibit interrupts until runWorker    this.firstTask = firstTask;    this.thread = getThreadFactory().newThread(this);}/** Delegates main run loop to outer runWorker. */public void run() {    runWorker(this);}

从代码里可以看出,Worker在创建线程Thread时,用的是自身;而不是原始的任务firstTask,这样一来,就得到了干预线程执行的机会。
4.在runWorker方法的主要代码如下:

task = w.firstTask;try{    while (task != null || (task = getTask()) != null){        task.run();    }} finally {    processWorkerExit(w, completedAbruptly);}

5.在addWorker方法中,做了两件事:创建线程并执行;即:
1.根据Runnable创建Worker对象;
2.将Worker对象存入数组workers中;
3.执行Worker对象里的thread;

6.根据线程的实现,我们知道t.start()的执行最终会调用Runnable里的run方法;由此可知在addWorker方法执行线程,最终会走到runWorker方法里来;
7.从runWorker方法的实现可以看到,每个入参Worker中的线程在完成自身的firstTask任务之后,还要继续去完成队列中的任务;
8.在runWorker方法中,当执行完所有的任务或者当执行完一个任务后发现线程已被中断,则会跳出循环,调用processWorkerExit方法来自动终结自己。
9.processWorkerExit方法里主要做了两件事:
1.将入参worker从数组workers里删除掉;
2.根据布尔值allowCoreThreadTimeOut来决定是否补充新的Worker进数组workers,方法为:

addWorker(null, false);

意思为只新增线程,无初始的任务。

回答前面的问题

1.线程池的工作原理是啥?
答:见原理图,先coresize,再queue队列,再maxsize,最后RejectedExecutionHandler。

2.核心线程池是一开始时就创建够吗?空闲时核心线程池也一直维持不变吗?
答:不是的,线程池是逐一增加至核心线程数的。不过可以通过executor提供的一些方法来直接将线程池预热至核心线程数。如方法:

public int prestartAllCoreThreads()

空闲时会根据allowCoreThreadTimeOut的值来决定是否要维持核心线程数的线程。该值默认是false,就是要维持核心线程池。可以通过方法来修改该布尔值。

3.线程池是如何被停止的?
答:通过shutdown或者shutdownNow方法来停止线程池。在这两个方法中,都是通过循环将works里每个work的线程进行Thread.interrupt()调用,给线程设置一个的停止标志。线程在自己的run方法中可以通过循环检查.interrupted()方法是否为真,来结束自己。

阅读全文
0 0
原创粉丝点击