线程池没你想的那么简单 (2)

在创建任务的时候提到过 worker.startTask() 函数:

/** * 添加任务,需要加锁 * @param runnable 任务 */ private void addWorker(Runnable runnable) { Worker worker = new Worker(runnable, true); worker.startTask(); workers.add(worker); }

也就是在创建线程执行任务的时候会创建 Worker 对象,利用它的 startTask() 方法来执行任务。

所以先来看看 Worker 对象是长啥样的:

线程池没你想的那么简单

其实他本身也是一个线程,将接收到需要执行的任务存放到成员变量 task 处。

而其中最为关键的则是执行任务 worker.startTask() 这一步骤。

public void startTask() { thread.start(); }

其实就是运行了 worker 线程自己,下面来看 run 方法。

线程池没你想的那么简单

第一步是将创建线程时传过来的任务执行(task.run),接着会一直不停的从队列里获取任务执行,直到获取不到新任务了。

任务执行完毕后将内置的计数器 -1 ,方便后面任务全部执行完毕进行通知。

worker 线程获取不到任务后退出,需要将自己从线程池中释放掉(workers.remove(this))。

从队列里获取任务

其实 getTask 也是非常关键的一个方法,它封装了从队列中获取任务,同时对不需要保活的线程进行回收。

线程池没你想的那么简单

很明显,核心作用就是从队列里获取任务;但有两个地方需要注意:

当线程数超过核心线程数时,在获取任务的时候需要通过保活时间从队列里获取任务;一旦获取不到任务则队列肯定是空的,这样返回 null 之后在上文的 run() 中就会退出这个线程;从而达到了回收线程的目的,也就是我们之前演示的效果

线程池没你想的那么简单

这里需要加锁,加锁的原因是这里肯定会出现并发情况,不加锁会导致 workers.size() > miniSize 条件多次执行,从而导致线程被全部回收完毕。

关闭线程池

最后来谈谈线程关闭的事;

线程池没你想的那么简单

还是以刚才那段测试代码为例,如果提交任务后我们没有关闭线程,会发现即便是任务执行完毕后程序也不会退出。

从刚才的源码里其实也很容易看出来,不退出的原因是 Worker 线程一定还会一直阻塞在 task = workQueue.take(); 处,即便是线程缩容了也不会小于核心线程数。

通过堆栈也能证明:

线程池没你想的那么简单

恰好剩下三个线程阻塞于此处。

而关闭线程通常又有以下两种:

立即关闭:执行关闭方法后不管现在线程池的运行状况,直接一刀切全部停掉,这样会导致任务丢失。

不接受新的任务,同时等待现有任务执行完毕后退出线程池。

立即关闭

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwsjs.html