前言
使用线程池有以下好处:
- 创建销毁线程消耗系统资源,线程池可以复用已创建的线程。
- 控制并发的数量,并发数量过多,可能导致资源不足,服务器崩溃。
- 对线程进行统一管理。
简单使用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;
public class Client {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
5,
10,
60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
Executors.defaultThreadFactory(),
new AbortPolicy()
);
executorService
.execute(() -> System.out.println(Thread.currentThread().getName() + " is running"));
executorService.shutdown();
}
}
参数分析
线程池的核心实现为ThreadPoolExecutor类,
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
一共有7大重要参数
- corePoolSize
表示核心线程数量,线程池中有两种线程,核心线程和非核心线程(临时工)
- maximumPoolSize
表示线程总数量,核心数+非核心数
- keepAliveTime
表示非核心线程闲置超时时长,超过了这个时间,线程就会被回收,如果设置了allowCoreThreadTimeOut(true),也会作用于核心线程
- unit
时间单位,主要有时(HOURS),分(MINUTES),秒(SECONDS),毫秒(MILLISECONDS),微妙(MICROSECONDS),纳秒(NANOSECONDS)
- workQueue
任务队列,当任务数量超过核心线程数时,就会将任务添加到任务队列中。
常用的队列类型有
1.LinkedBlockingQueue,阻塞队列,底层实现为链表,默认大小为Integer.MAX_VALUE,可以设置大小
2.ArrayBlockingQueue,阻塞队列,底层实现为数组,必须指定大小
3.SynchronousQueue,同步队列,容量为0,添加操作必须等待消费操作,反之亦然
4.DelayQueue,延时队列,只有到达了延时时间,才能从队列中获取到
- threadFactory
表示创建线程的工厂,默认实现为
/**
* The default thread factory.
*/
private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
- handler
表示拒绝策略,当任务数量大于线程最大容量时,就会执行拒绝策略,有4种实现
1.AbortPolicy,直接抛异常
2.DiscardPolicy,直接丢弃,不抛异常
3.DiscardOldestPolicy,丢弃任务队列头部元素,重新尝试执行
4.CallerRunsPolicy,由调用线程来执行
上述7个参数,前5个为必须,后2个非必须。
线程池的处理流程
- 线程总数量 < corePoolSize,⽆论线程是否空闲,都会新建⼀个核心线程执⾏任务。
- 线程总数量 >= corePoolSize时,新来的线程任务会进⼊任务队列中等待,然后空闲的核心线程会依次去缓存队列中取任务来执行(体现了线程复用)。
- 当缓存队列满了,说明这个时候任务已经多到爆棚,需要⼀些“临时⼯”来执⾏ 这些任务了。于是会创建⾮核心线程去执行这个任务。
- 缓存队列满了,且总线程数达到了maximumPoolSize,则会执行拒绝策略。
线程池状态
线程池一共有5种状态,
- RUNNING,线程池创建后为RUNNING状态
- SHUTDOWN,调用shutdown()方法后处于SHUTDOWN状态,此时不能接收新任务,中断所有未执行的线程,等待任务队列中任务完成。
- STOP,调用shutdownNow()方法后处于STOP状态,此时不能接收新任务,中断所有正在执行的线程,任务队列中未执行任务全部丢弃。
- TIDYING,当所有任务已终止,线程池中任务数量为0,此时状态为TIDYING
- TERMINATED,执行完terminated()方法,状态由TIDYING变成TERMINATED
4种常用的线程池
jdk提供了一个工具类Executors,简化我们创建线程池的过程
- newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
不会创建核心线程,全部为非核心线程(临时工),超过60S回收。适合执行有很多短时间的任务。
- newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
只创建核心线程,不创建非核心线程,超过数量全部进任务队列。
- newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
只有一个核心线程,和 newFixedThreadPool(1) 的区别为,newFixedThreadPool(1)创建的线程池是可以重新配置的,如重新设置核心线程数量,而newSingleThreadExecutor()创建的线程池不能重新配置。
- newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
ScheduledThreadPoolExecutor继承于ThreadPoolExecutor,支持定时任务及周期性任务。