一般我们进行线程的操作时,往往要创建一个新的线程,执行完毕后再销毁,等到有新的执行命令时,又得重新创建线程,如此一来显得十分繁琐,如果我们将之前执行过的线程不销毁而是放入一个池子中,当需要执行时直接引用它,这就能省下许多操作的步骤与时间,因此不会由于等待创建线程而延迟任务的执行,从而提高了响应性,同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存而失败,提高线程的可管理性。
一般一个简单线程池至少包含下列组成部分。
1. 线程池管理器(ThreadPoolManager):用于创建并管理线程池
2. 工作线程(WorkThread): 线程池中线程
3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
4. 任务队列:用于存放没有处理的任务。提供一种缓冲机制。
线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务
public class ThreadPoolManager {
private int threadCount; //启动的线程数
private WorkThread[] handlers; //线程数组
private ArrayList<Runnable> taskVector = new ArrayList<Runnable>(); //任务队列
ThreadPoolManager(int threadCount) {
this.threadCount = threadCount;
for (int i = 0; i < threadCount; i++) {
handlers[i] = new WorkThread();
handlers[i].start();
}
}
void shutdown() { //关闭打开的线程和清空还没有执行的任务
synchronized (taskVector) {
while (!taskVector.isEmpty())
taskVector.remove(0); //清空任务队列
}
for (int i = 0; i < threadCount; i++) {
handlers[i] = new WorkThread();
handlers[i].interrupt(); //结束线程
}
}
void execute(Runnable newTask) { //增加新任务
synchronized (taskVector) {
taskVector.add(newTask);
taskVector.notifyAll();
}
}
private class WorkThread extends Thread {//实际的工作线程
public void run() {
Runnable task = null;
for (;;) {
synchronized (taskVector) {//获取一个新任务
if (taskVector.isEmpty())
try {
taskVector.wait();
task = taskVector.remove(0);
}
catch (InterruptedException e) {
break;
}
}
task.run();
}
}
}
}
ThreadPoolExecutor类
public class ThreadPoolExecutor extends AbstractExecutorService { ... }
public abstract class AbstractExecutorService implements ExecutorService { ... }
public ThreadPoolExecutor(int corePoolSize,//线程池的基本大小
int maximumPoolSize,//最大值
long keepAliveTime,//存活时间
TimeUnit unit,//参数keepAliveTime的时间单位,有7种取值
BlockingQueue<Runnable> workQueue,//一个阻塞队列,存储等待执行的任务
ThreadFactory threadFactory,//创建新线程时使用的工厂
RejectedExecutionHandler handler//表示当拒绝处理任务时的策略
){ ... }
execute()方法:是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
submit()方法:是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果(Future相关内容将在下一篇讲述)。
shutdown()和shutdownNow()是用来关闭线程池的。
下面用两个例子介绍线程池的使用方法,第一个例子会创建一个固定大小的线程池,第二个例子会创建基于时间线程池。
第一个例子 :
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建四个任务
Thread t1 = new Task1();
Thread t2 = new Task2();
Thread t3 = new Task3();
Thread t4 = new Task4();
//放入线程池
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.shutdown(); //关闭线程池
第二个例子:
ExecutorService pool = Executors.newScheduledThreadPool(4);
Thread t = new Task();
pool.scheduleAtFixedRate(t,1, 5, TimeUnit.SECONDS);