最近在写Java程序的时候,接触到一些多线程方面的东西,用到了Java中的线程池。JDK中对线程池的支持比较完善,在java.util.concurrent
包中,用ThreadPoolExecuter
类表示一个线程池,同时还有一个Executor
类扮演着线程池工厂的角色。例如:
1 | public static ExecutorService newFixedThreadPool(int nThreads) |
这些工厂方法,从本质上,都是调用了ThreadPoolExecutor
类的构造函数:
1 | public ThreadPoolExecutor(int corePoolSize, |
参数含义如下:
- corePoolSize:线程池中应该保持的线程数量,即使线程空闲
- maximumPoolSize:线程池中最大线程的数量
- keepAliveTime:当线程数量大于corePoolSize时,指定空闲线程存在多久会被销毁
- unit:keepAliveTime的单位
- workQueue:任务队列,被提交但还没有执行
- threadFactory:线程工厂
- handler:拒绝策略
实现思路
从ThreadPoolExecutor
的构造函数中,我们大概知道实现一个线程池需要哪些东西,如果完全按照构造函数中的参数来的话,太麻烦,有太多地方需要考虑,因此实现一个简单版的。
- 首先需要考虑线程池中存放多少线程。可以简单用一个变量来指定,并且这些线程要放在一个容器里,便于销毁,也便于知道他们的状态。
- 然后我们要考虑一个作为任务队列的容器。假如线程池中有5个线程,如果5个线程都处于工作状态的话,这时候送来的任务就需要放在任务队列中等待。
- 最后是线程池中工作线程的形式。工作线程在创建时开始就应该启动,其所做的工作主要是:
从任务队列中取出任务-执行任务
这样的无限循环。
工作线程的一种实现方式:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class WorkerThread extends Thread {
private Boolean isRunning = true;
public void close() {
isRunning = false;
}
public void run() {
while (isRunning) {
Runnable task = null;
try {
task = taskQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
task.run();
}
}
}
WorkerThread是线程池的内部类,其中,taskQueue
是任务队列,这里采用了BlockingQueue
接口的一种实现,其put
和take
方法都是阻塞的。提交任务和销毁线程池如下:
1 | public void submit(Runnable task) { |
完整代码在这里。
线程池看似简单,其实很复杂,因为如果真要到一个应用级别的话,要考虑的东西还有很多,例如何时该启动一个线程,何时线程应该中止与挂起,任务队列的阻塞与超时,任务拒绝策略,线程生命周期等。至于本文实现的线程池,just for fun~