java 中的池化技术可以在我们开辟新的线程之前提前为我们准备好,从而减少了系统的开销。线程池帮我们对线程统一管理,同时也提高了系统的响应。jdk concurrent 中的 Executors 类为我们提供了创建线程池的方法,简单易用。主要有以下几个方法:
- newFixedThreadPool(int nThreads) 固定线程数的线程池;
- newSingleThreadExecutor() 只产生单个线程的线程池;
- newCachedThreadPool 缓存类型的线程池,线程数量可伸缩,非固定;
先来说下,除了这些 jdk 给我们封装好的方法外,我们可以自己手动创建线程池,自己创建可以加深我们对这部分知识的印象,我们可以通过调用 ThreadPoolExecutor 的构造方法来创建:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
看下他的重载构造器:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
它的的构造方法中,主要有这么几个重要的参数:
- corePoolSize 线程池的核心线程数;
- maximumPoolSize 线程池的最大线程数;
- keepAliveTime 闲置线程存货时间,超过这个时间将会被回收(超过核心线程数的部分);
- unit 时间单位;
- workQueue 阻塞队列,当线程数超过核心线程数时,任务将会被放入队列中;
- threadFactory 线程工厂,用于创建线程,一般使用它默认的内部类,ThreadPoolExecutor 没有提供该字段的 setter/getter方法;
- handler 拒绝策略,当阻塞队列满,并且超过最大线程数时,将会执行拒绝策略的 reject 方法。
看一下线程池执行线程的 execute 方法:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 如果工作线程数小于核心线程数,调用 addWorker 方法后直接返回
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 超过了核心线程数,优先添加到阻塞队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
// 阻塞队列满,新建线程(每个线程都是一个 worker)并添加到 workers。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 阻塞队列满,并且线程数大于最大线程数,调用 handler 的 rejectedExecution 方法。
else if (!addWorker(command, false))
reject(command);
}
再来看下线程池的拒绝策略,ThreadPoolExecutor,有个属性 defaultHandler,即默认的拒绝策略。该类型为 AbortPolicy:其 rejectedExecution 方法是抛出异常。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
DiscardPolicy:什么都不做,放弃本次的任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
DiscardOldestPolicy:抛弃前面的任务,把新的任务加入到缓存队列中:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
// 弹出最前面的任务
e.getQueue().poll();
// 执行新的任务
e.execute(r);
}
}
CallerRunsPolicy:由调用线程执行 run 方法:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
// 可以看到只是当前线程调用了 run 方法,并没有新建线程。
r.run();
}
}
小结:
主要简述了线程池常用的创建方法、ThreadPoolExecutor 的构造器及七个参数的意义,同时也阐述了四种拒绝策略的作用及原理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。