为什么用线程池?
频繁的创建和销毁线程需要时间,会大大降低系统的效率。线程池就是一个容纳多个线程的 容器,池中的线程可以反复使用,节省了大量的时间和资源。
使用线程池主要为了解决以下几个问题:
- 通过重用线程池中的线程,来减少每个线程创建和销毁的性能开销。
- 对线程进行一些维护和管理,比如定时开始,周期执行,并发数控制等等。
创建和使用线程池:
创建线程池有以下两种方法:
- 通过 Executors 类的静态方法
- 通过 ThreadPoolExecutor 的构造方法,自定义线程池
通过看 Executors 的源码可知,Executors 类创建线程池的时候实质就是调用 ThreadPoolExecutor 类的构造方法来创建。除此之外,阿里巴巴Java开发手册中不允许使用 Executors 来创建线程池,而是通过 ThreadPoolExecutor 的方式。
使用 ThreadPoolExecutor 构造方法来创建线程池及参数详解:
ThreadPoolExecutor 源码:
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;
}
参数 1 :corePoolSize 核心线程数量,即使闲置,也仍然会存活。默认情况下核心线程会一直存活,除非将 allowCoreThreadTimeOut设置为 true
。
参数 2:maximumPoolSize 最大线程数量,线程池里能容下的最大线程数,包括核心线程和非核心线程。
参数 3:keepAliveTime 线程存活时间。当线程池中的线程数大于核心线程数时,keepAliveTime 表示空闲的非核心线程数 能存活的最大时长。
参数 4:unit 参数3的时间单位
参数 5:BlockingQueue 阻塞队列,核心线程已满,但最大线程未满的情况下,新创建的线程会被放入阻塞队列中,常用的有三种队列,SynchronousQueue,
LinkedBlockingDeque,
ArrayBlockingQueue
。
参数 6:ThreadFactory 线程工厂,ThreadFactory是一个接口,只有一个方法,提供创建新线程的功能。
public interface ThreadFactory {
Thread newThread(Runnable r);
}
参数7:RejectedExecutionHandler 拒绝策略,当最大线程数已满时,新创建的线程会被拒绝策略拒绝。具体有一下几种策略:
不直接丢弃任务的两种策略:
AbortPolicy:抛出RuntimeException,会中断该线程的调用过程。
CallerRunsPolicy:线程不进入线程池,在线程池外由调用者线程去执行。
直接丢弃任务的两种策略:
DiscardPolicy:丢弃当前要加入队列的新线程。
DiscardOldestPolicy:丢弃队列中最旧的线程。
使用线程池:不必通过 Thread或Runnable 显式地创建线程,直接给线程分配任务即可
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(4,4,10, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
// 给线程池中的线程添加任务
executor.execute(new Runnable() {
@Override
public void run() {
}
});