线程池是并发编程比较典型的知识点了.当你想在应用中限制同一时间并行执行线程数量的时候可以使用线程池.对比通过创建新线程的方式线程池有显著的性能提升且每一个线程都能获得一定大小的内存空间用作线程栈存储.
可以将任务通过传入线程池来代替创建新线程的方式让任务并行执行.当线程池有闲置线程时,任务将会被分配给这部分线程执行.在线程池内部任务通过插入阻塞队列的方式来让闲置线程取出执行.当有新任务到达时,将会由一个闲置线程成功取出后执行.其他闲置线程会在线程池中阻塞待命,直到有新的任务到达.
线程池常用在多线程服务器上.每一个链接通过网络到达服务器时都会被当作一个任务进入到线程池里.线程池中的线程将会并行处理链接中的请求.
Java5的java.util.concurrent包中已经有现成的实现,因此我们不需要自己手动实现线程池.但我们仍然很有必要知道它的底层实现细节.
这里提供了一个简单的线程池实现.你会发现我们使用了前文阻塞队列中的实现.然而在正式环境中,我们可以使用Java的内建阻塞队列来替代.
public class ThreadPool {
private BlockingQueue<Runnable> taskQueue;
private List<PoolThread> threads = new ArrayList<>();
private boolean isStopped = false;
public ThreadPool(int initThreads, int maxTasks) {
taskQueue = new BlockingQueue<>(maxTasks);
IntStream.range(0, initThreads)
.mapToObj(i -> new PoolThread(taskQueue))
.forEach(threads::add);
threads.forEach(PoolThread::start);
}
public synchronized void execute(Runnable task) {
if (isStopped) throw new IllegalStateException("ThreadPool is stopped");
taskQueue.enqueue(task);
}
public synchronized void stop() {
isStopped = true;
threads.forEach(PoolThread::doStop);
}
private class PoolThread extends Thread {
private BlockingQueue<Runnable> taskQueue;
private boolean isStopped = false;
public PoolThread(BlockingQueue<Runnable> taskQueue) {
this.taskQueue = taskQueue;
}
@Override
public void run() {
while (!isStopped()) {
try {
Runnable runnable = taskQueue.dequeue();
runnable.run();
}catch (Exception e){
e.printStackTrace();
}
}
}
public synchronized void doStop() {
isStopped = true;
Thread.currentThread().interrupt(); // break pool thread of dequeue() call.
}
public synchronized boolean isStopped() {
return isStopped;
}
}
}
一个线程池的实现有两部分.一个是ThreadPool.class来提供对外的入口.而PoolThread则是继承了Thread用于创建线程池中的线程来调度任务.
一个任务可以通过ThreadPool实例的execute(Runnable task)来传入.execute()接受一个Runnable作为参数.而Runnable则是任务的载体,任务的执行代码可以写在Runnable的run方法里.Runnable在线程池内部会被添加到阻塞队列中,等待调度.
阻塞队列中的Runnable会被闲置的PoolThead取出和执行.你可以注意到PoolThread实例中的run()方法.队列为空时线程会阻塞在dequeue()调用上,直到有新的任务进入队列线程才被唤醒,从而取出任务执行,这个过程会持续到线程停止为止.
停止线程池可以通过ThreadPool实例的stop()方法.我们会注意到stop()方法内部将isStopped变量置换为true且依次调用线程池中的每一个线程的doStop()方法.当调用stop()完成后,再次调用execute()方法将会抛出IllegalStateException异常.
线程会在并行执行完任务后停止.我们注意到PoolThread实例doStop()方法中的Thread.currentThread().interrupt()调用.这能够让阻塞在taskQueue.dequeue()中wait()调用上的线程退出wait()调用,从而退出dequeue()方法抛出一个InterruptedException异常.这个异常会在PoolThread实例的run()方法中捕获,从而重新进入while(!isStopped)检查,从而退出run()方法结束线程.
该系列博文为笔者复习基础所著译文或理解后的产物,复习原文来自Jakob Jenkov所著Java Concurrency and Multithreading Tutorial