Life's monolog

Java线程池的简单实现

Word count: 762 / Reading time: 3 min
2016/07/20 Share

最近在写Java程序的时候,接触到一些多线程方面的东西,用到了Java中的线程池。JDK中对线程池的支持比较完善,在java.util.concurrent包中,用ThreadPoolExecuter类表示一个线程池,同时还有一个Executor类扮演着线程池工厂的角色。例如:

1
2
3
4
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
...

这些工厂方法,从本质上,都是调用了ThreadPoolExecutor类的构造函数:

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

参数含义如下:

  • corePoolSize:线程池中应该保持的线程数量,即使线程空闲
  • maximumPoolSize:线程池中最大线程的数量
  • keepAliveTime:当线程数量大于corePoolSize时,指定空闲线程存在多久会被销毁
  • unit:keepAliveTime的单位
  • workQueue:任务队列,被提交但还没有执行
  • threadFactory:线程工厂
  • handler:拒绝策略

实现思路

ThreadPoolExecutor的构造函数中,我们大概知道实现一个线程池需要哪些东西,如果完全按照构造函数中的参数来的话,太麻烦,有太多地方需要考虑,因此实现一个简单版的。

  1. 首先需要考虑线程池中存放多少线程。可以简单用一个变量来指定,并且这些线程要放在一个容器里,便于销毁,也便于知道他们的状态。
  2. 然后我们要考虑一个作为任务队列的容器。假如线程池中有5个线程,如果5个线程都处于工作状态的话,这时候送来的任务就需要放在任务队列中等待。
  3. 最后是线程池中工作线程的形式。工作线程在创建时开始就应该启动,其所做的工作主要是:从任务队列中取出任务-执行任务这样的无限循环。

工作线程的一种实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class 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接口的一种实现,其puttake方法都是阻塞的。提交任务和销毁线程池如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void submit(Runnable task) {
if (currentWorking() < limits) {
WorkerThread worker = new WorkerThread();
worker.start();
workers.add(worker);
}
try {
taskQueue.put(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void destroy() {
while (!taskQueue.isEmpty());
for (WorkerThread worker : workers) {
worker.close();
}
}

完整代码在这里

线程池看似简单,其实很复杂,因为如果真要到一个应用级别的话,要考虑的东西还有很多,例如何时该启动一个线程,何时线程应该中止与挂起,任务队列的阻塞与超时,任务拒绝策略,线程生命周期等。至于本文实现的线程池,just for fun~

CATALOG
  1. 1. 实现思路