线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们所知的实现了 Runnable 或 Callable 接口的实例对象。
线程池的优势:
为什么要使用线程池?
jdk 自带的四种线程池
Java 通过 Executors 提供了四种线程池,分别是:
newCachedThreadPool
创建一个可缓存线程池,如果线程池长度不超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超过的线程会在队列中等待。newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。通过 Executors. 上面的静态方法得到的连接池都有一个共同点,就是他们都是去 new ThreadPoolExecutor
然后进行返回。
接下来对 ThreadPoolExecutor 构造方法的每个操作进行解释:
参数 | 意义 |
---|---|
corePoolSize | 指定了线程池里的线程数量,核心线程池大小(0-Integer.MAX_VALUE) |
maximumPoolSize | 指定了线程池里的最大线程数量(0-Integer.MAX_VALUE) |
keepAliveTime | 当线程池线程数量大于corePoolSize时候,多出来的空闲线程,多长时间会被销毁(0-Integer.MAX_VALUE) |
unit | 时间单位,TimeUnit |
workQueue | 任务队列(阻塞任务队列),用于存放提交但是尚未被执行的任务 |
threadFactory | 线程工厂,用于创建线程,线程工厂就是给我们new线程的 |
handler | 所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略 |
常见的工作队列(对应参数5)我们有如下选择,这些阻塞队列,阻塞队列的意思是,当队列中没有值的时候,取值操作会阻塞,一直等队列中产生值。
线程池提供的四种拒绝策略(对应参数7)
了解完 ThreadPoolExecutor 构造方法的各个参数之后,来看看 jdk 为我们提供的四个线程池是如何实现的吧:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
来看看其参数:
核心线程池大小为:0
最大线程数支持:Integer.MAX_VALUE
线程过期时间为60s
使用了 SynchronousQueue 作为工作队列。
拒绝策略没有指定,默认是抛出异常。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
来看看其参数:
核心线程数和最大线程数需咱指定,都是相同的,也就是说不支持缓存线程。
没有缓存线程,自然过期时间就是0了。
工作队列使用的是 LinkedBlockingQueue
拒绝策略没有指定,默认是抛出异常。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
核心线程池大小=传入参数
最大线程池大小为Integer.MAX_VALUE
线程过期时间为0ms
DelayedWorkQueue作为工作队列.
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
核心线程池大小=1
最大线程池大小为1
线程过期时间为0ms
LinkedBlockingQueue作为工作队列.
这里是针对JDK1.8版本,使用JDK自带的线程池会出现OOM问题,中小型公司一般很难遇到,在阿里巴巴开发文档上面有明确的标识:
上面解释了 ThreadPoolExecutor 的构造参数,那现在自己自定义的话其实很简单,本质的话就是自定义 ThreadFactory(实现里面的 newThread 方法即可),如下所示:
public static void main(String[] args) {
AtomicInteger num = new AtomicInteger(0);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
30,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
/*Executors.defaultThreadFactory()*/new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("myThread-" + num.getAndIncrement());
thread.setDaemon(false);
return thread;
}
},
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 100; i++) {
threadPoolExecutor.submit(() -> {
System.out.println(Thread.currentThread() + "-----------------");
});
}
}