线程池的工作原理
我认为线程池它就是一个调度任务的工具。
众所周知在初始化线程池会给定线程池的大小,假设现在我们有 1000 个线程任务需要运行,而线程池的大小为 10,20 个线程来调度这1000个任务。
而这里的 10~20 个线程最后会由线程池封装为 ThreadPoolExecutor.Worker
对象,而这个 Worker
是实现了 Runnable 接口的,所以他自己本身就是一个线程。
深入分析
ExecutorService executorService = new ThreadPoolExecutor(2,2,0L,TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2));
这里我们来做一个模拟,创建了一个核心线程、最大线程数、阻塞队列都为2的线程池。
这里假设线程池已经完成了预热,也就是线程池内部已经创建好了两个线程 Worker
。
当我们往一个线程池丢一个任务会发生什么事呢?
- 第一步是生产者,也就是任务提供者他执行了一个 execute() 方法,本质上就是往这个内部队列里放了一个任务。
- 之前已经创建好了的 Worker 线程会执行一个
while
循环 ---> 不停的从这个内部队列
里获取任务。(这一步是竞争的关系,都会抢着从队列里获取任务,由这个队列内部实现了线程安全。) - 获取得到一个任务后,其实也就是拿到了一个
Runnable
对象(也就是execute(Runnable task)
这里所提交的任务),接着执行这个Runnable
的 run() 方法,而不是 start(),这点需要注意后文分析原因。
为什是 run() 而不是 start()
为什么线程池在调度的时候执行的是 Runnable
的 run()
方法,而不是 start()
方法呢?
网上大牛讲的都是只有执行了 start()
方法后操作系统才会给我们创建一个独立的线程来运行,而 run()
方法只是一个普通的方法调用。
而在线程池这个场景中却恰好就是要利用它只是一个普通方法调用。
假设这里是调用的 Runnable
的 start
方法,那会发生什么事情。
如果我们往一个核心、最大线程数为 2 的线程池里丢了 1000 个任务,那么它会额外的创建 1000 个线程,同时每个任务都是异步执行的,一下子就执行完毕了。
从而没法做到由这两个 Worker
线程来调度这 1000 个任务,而只有当做一个同步阻塞的 run()
方法调用时才能满足这个要求。