多线程应用程序中,线程的创建和销毁开销相对较高。每次创建线程都需要操作系统分配资源,销毁线程也需要释放资源。线程池就是为了解决这个问题,如果某个线程执行完自己的“任务”之后,并不是将线程释放,而是放到一个“池子”中,下次如果需要用到线程继续执行任务的话直接从池子中取,这样就不用再去重复的创建销毁。
Java中线程池的底层真正实现是通过ThreadPoolExecutor来实现的,在使用的时候提供了Executors类供开发者使用,并提供了一系列的功能线程池。所以先来了解ThreadPoolExecutor的底层代码,然后再来分析Executors。
通过追源码,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) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
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;
}
这是官方API文档的描述
可以看到构造方法中有以下这些参数,参数有的是必需的,有的是可选的,现在理解一下这些参数:
必需参数
可选参数
当线程池的线程数达到最大线程数时,需要执行拒绝策略。
Executors 为我们实现了 4 种拒绝策略:
- AbortPolicy():超过负荷,直接抛出异常(默认策略).
- CallerRunsPolicy():调用者负责处理.
- DiscardOldestPolicy():丢弃队列中最早的任务,将新任务加入.
- DiscardPolicy():丢弃新来的任务.
简单描述一下线程池的工作原理,对上面的参数有具体的认识:
代码示例:
public class Demo1 {
public static void main(String[] args) {
//创建线程池(传入参数)
ExecutorService pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//向线程池提交任务
for (int i = 0; i < 3; i++) {
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
}
}
由于线程大于了线程池最大线程数,可以看到代码触发了拒绝策略,我们使用的是AbortPolicy(),所以超负荷就直接抛出异常。将循环中的3减少为2 就不会抛出异常。
Java对ThreadPoolExecutors进行了封装,方便使用者对线程池进行使用,Executors根据不同的使用场景提供了下面四种创建线程池的方式:
具体地
特点:
动态大小的线程池。
没有核心线程,核心线程数为0,最大线程数为Integer.MAX_VALUE。
使用SynchronousQueue作为阻塞队列,可无限扩展。
非核心线程的存活时间为60秒,即空闲60秒后的线程将被终止。
使用场景:
适用于需要处理大量短时任务的情况,例如任务执行时间不定,需要动态分配线程。