章
目
录
在讲解Java创建线程池的几种方式之前,潘老师先带大家了解一些关于线程池的概念和原理。
一、Java线程池是什么
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务,并且线程池中的线程都是后台线程。
二、Java线程池作用意义
第1:降低资源消耗。通过重复利用机制已降低线程创建和销毁造成的消耗。
第2:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第3:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
三、Java四种线程池的使用
JDK1.5中引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。Java通过Executors为我们提供了四种线程池的创建方式,分别为:
1)newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
// 可缓存线程池 Executors表示启动线程的 可创建线程数是无限大小的
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int temp = i;
// 可执行线程 execute 启动线程
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "," + temp);
}
});
}
//停止线程池
executorService.shutdown();
2)newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
//创建可固定长度的线程池,只会创建5个线程池进行处理
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
final int temp = i;
// 可执行线程 execute 启动线程
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "," + temp);
}
});
}
//停止线程池
executorService.shutdown();
3)newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
//创建可定时执行的线程池 数量为3
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
for (int i = 0; i < 10; i++) {
final int temp=i;
//schedule 方法表示线程执行 表示延迟5秒之后 开始执行线程
scheduledExecutorService.schedule(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+""+temp);
}
}, 5, TimeUnit.SECONDS);
}
//停止线程池
scheduledExecutorService.shutdown();
4)newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
//创建单线程线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int temp = i;
// 可执行线程 execute 启动线程
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "," + temp);
}
});
}
//停止线程池
executorService.shutdown();
查看Executors类源码,以newSingleThreadExecutor 方法为例:

我们发现其实都是在使用ThreadPoolExecutor类进行构建的,然后该类目构造方法有很多参数。这些参数很重要,需要掌握,面试也会经常被问到!
四、ThreadPoolExecutor线程池类7大参数详解
| 参数 | 含义 |
| corePoolSize | 核心线程数量,线程池维护线程的最少数量 |
| maximumPoolSize | 线程池维护线程的最大数量 |
| keepAliveTime | 线程池除核心线程外的其他线程的最长空闲时间,超过该时间的空闲线程会被销毁 |
| unit | keepAliveTime的单位,TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS |
| workQueue | 线程池所使用的任务缓冲队列 |
| threadFactory | 线程工厂,用于创建线程,一般用默认的即可 |
| rejectHandler | 线程池拒绝任务时的处理策略 |
五、Java线程池执行流程
一个线程提交到线程池后的具体处理流程如下图所示:

1)如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2)如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
3)如果此时线程池中的数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4)如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
5)当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
总结:处理任务判断的优先级为 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler采取任务拒绝策略进行处理;
线程池的拒绝策略:当最大线程数 + 队列缓存数量 小于线程数量的时候,程序运行出错,被拒绝。(在此不再详细展开)
六、Java自定义线程池
虽然JDK的Executors提供了4大类线程池供我们直接使用,但是在实际生产开发中,往往是不允许/不建议使用这4种直接调用的方式的,比如在阿里巴巴的Java开发手册中就有如下【强制】要求:

原因就是因为直接创建会容易导致OOM(内存溢出~)
推荐我们手工自定义线程池,在自定义时我们需要预估线程数多少合适,主要参考依据为核心线程数计算公式:
- IO密集型:核心线程数 = CPU核数 / (1-阻塞系数)
- CPU密集型:核心线程数 = CPU核数 + 1
- IO密集型:核心线程数 = CPU核数 * 2
可以创建个工具类如下:
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolUtil {
/**
* 默认 CPU 核心数
*/
private static int threadPoolSize = 0;
static {
// 获取服务器 CPU 核心数
threadPoolSize = Runtime.getRuntime().availableProcessors();
System.out.println(" CPU 核心数量:" + threadPoolSize);
}
public static int getThreadPoolSize() {
return threadPoolSize;
}
/**
* 线程工厂,用来创建线程
*/
private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-demo-%d").setDaemon(true).build();
/**
* 设置线程池核心参数(IO 密集型) 核心线程数 = CPU 核数 * 2
* 参数依次分别为上面的的7大参数,最后拒绝策略参数可以不写,则默认使用AbortPolicy
*/
private static ThreadPoolExecutor threadPoolExecutorIO =
new ThreadPoolExecutor(threadPoolSize, threadPoolSize * 2, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());
/**
* 设置线程池核心参数(CPU 密集型) 核心线程数 = CPU 核数 + 1 // 核心线程数 = CPU 核数 + 1
*/
private static ThreadPoolExecutor threadPoolExecutorCPU =
new ThreadPoolExecutor(threadPoolSize, threadPoolSize + 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());
/**
* 返回线程池对象
*
* @return
*/
public static ThreadPoolExecutor getThreadPoolExecutorIO() {
return threadPoolExecutorIO;
}
public static ThreadPoolExecutor getThreadPoolExecutorCPU() {
return threadPoolExecutorCPU;
}
}
如果你不想区分CPU密集型还是IO密集型,只是想简单的自己手工创建一个线程池,比如我这里只想创建一个SingleThreadExecutor,则代码如下:
ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), new ThreadFactoryBuilder().setNameFormat("thread-pool-demo-%d").setDaemon(true).build());
for (int i = 0; i < 10; i++) {
final int temp = i;
// 可执行线程 execute 启动线程
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "," + temp);
}
});
}
以上就是Java创建线程池的几种方式具体实现全部内容,如果你有什么疑问,可以评论留言~





