什么是线程池?
WIKI:
线程池(ThreadPool)是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
为什么要使用线程池?
在Java中,要启动一个线程,通常有两种方式:
- 继承Thread类。
- 实现Runnable接口。
这么做会有以下缺点:
- 每次new Thread新建对象性能差。
- 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
- 缺乏更多功能,如定时执行、定期执行、线程中断。
线程池的优点:
- 重用存在的线程,减少对象创建、消亡的开销,性能佳。
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
如何使用线程池?
Java中提供了ThreadPoolExecutor类,此类提供了许多构造函数,可通过如下方式创建使用线程池。
public class ThreadPoolDemo {
static AtomicInteger threadNumber = new AtomicInteger(1);
public static void main(String[] args) {
int corePoolSize = 2;
int maximumPoolSize = 5;
long keepAliveTime = 60;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
// 自己制定规则
return new Thread(Thread.currentThread().getThreadGroup(), r,
"线程" + threadNumber.getAndIncrement(),
0);
}
},
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
throw new RuntimeException();
}
}
);
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " -- executed");
});
}
threadPoolExecutor.shutdown();
}
}
ThreadPoolExecutor构造函数参数说明,实际使用时选择合适的构造函数即可:
- corePoolSize,核心线程数量,线程池的基本大小,即在没有任务需要执行的时候线程池的大小。
- maximumPoolSize,线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。
- keepAliveTime,无任务执行时,最多保持多久时间终止。
- unit,keepAliveTime的时间单位。
- workQueue,阻塞队列,根据业务场景选择合适的阻塞队列。
- threadFactory,线程工厂。
- rejectHandler,拒绝任务时的策略。
ThreadPoolExecutor类常用方法
- execute,提交任务给线程池执行。
- submit,提交任务,能够返回执行结果
- shutdown,等待任务执行完后,关闭线程池。
- shutdownNow,关闭线程池,不等待任务执行完。
- getTaskCount,已执行和未执行的任务总数。
- getActiveCount,正在执行的任务总数。
- getPoolSize,线程池当前的线程数量。
- getCompletedTaskCount,已完成的任务数量。