在 Java 语言中,并发编程往往都是通过床架线程池来实现的,而线程池的创建方式也有很多种,每种线程池的创建方式都对应了不同的使用场景。总结来说线程池的创建可以分为两大类:
通过 Executors 创建
通过 ThreadPoolExecutor 创建
以上这两类创建线程池的方式有 7 种具体实现方法,这 7 种方法便是本文要说的创建线程池的七种方式。分别是:
方法 | 含义 |
---|---|
Executors.newFixedThreadPool() | 创建一个大小固定的线程池,可控制并发的线程数,超出的线程会在队列中等待 |
Executors.newCachedThreadPool() | 创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程 |
Executors.newSingleThreadExecutor() | 创建单个线程的线程池,可以保证先进先出的执行顺序 |
Executors.newScheduledThreadPool() | 创建一个可以执行延迟任务的线程池 |
Executors.newSingleThreadScheduledExecutor() | 创建一个单线程的可以执行延迟任务的线程池 |
Executors.newWorkStealingPool() | 创建一个抢占式执行的线程池 |
ThreadPoolExecutor() | 手动创建线程池,可自定义相关参数 |
Executors.newFixedThreadPool():创建一个固定大小的线程池,可控制并发的线程数。
public class FixedThreadPoolDemo {
public static void main(String[] args) {
// 创建 2 个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
// 创建任务
Runnable runnable = () -> System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
// 线程池执行任务(一次添加 8 个任务)
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
}
}
创建一个具有 2 个线程的线程池,执行 8 个任务,执行结果为:
Executors.newCachedThreadPool():创建一个可缓存的线程池,若线程数超过人物所需,那么多余的线程会被缓存一段时间后再回收,若线程数不够,则会新建线程。
public class CachedThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
// 执行任务
for (int i = 0; i < 5; i++) {
threadPool.execute(() -> {
System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
});
}
}
}
创建了一个具有 5 个线程的线程池来执行相应的任务。
使用场景
CachedThreadPool 是根据短时间的任务量来决定创建的线程数量的,所以它适合短时间内有突发大量任务的处理场景。
Executors.newSingleThreadExecutor():创建只有单个线程的线程池,可以保证先进先出的顺序。
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
// 创建线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 执行任务
for (int i = 0; i < 10; i++) {
int index = i;
threadPool.execute(() -> {
System.out.println(index + ": 任务被执行: " + Thread.currentThread().getName());
});
}
}
}
如果在打印语句下再加一行睡眠的语句,就会看到每个一段时间输出任务被执行的过程~
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Executors.newScheduledThreadPool():创建一个可以执行延迟任务的线程池。
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
// 添加定时执行任务(1s 后执行)
System.out.println("添加任务,时间:" + new Date());
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + new Date());
}, 2, TimeUnit.SECONDS);
}
}
创建一个延迟 2 秒执行任务的线程池。
Executors.newSingleThreadScheduledExecutor():创建一个单线程的可以执行延迟任务的线程池。这种线程池可以看做是 ScheduledThreadPool 的单线程版本。
public class SingleThreadScheduledExecutorDemo {
public static void main(String[] args) {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
// 添加定时执行任务(2s 后执行)
System.out.println("添加任务,时间:" + new Date());
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
}, 2, TimeUnit.SECONDS);
}
}
Executors.newWorkStealingPool():创建一个抢占式执行的线程池,执行任务的顺序不确定。需要注意的是此方法是 JDK 1.8 版本新增的,所以 1.8 版本之前的程序中不能使用。
public class WorkStealingPoolDemo {
public static void main(String[] args) {
// 创建线程池
ExecutorService threadPool = Executors.newWorkStealingPool();
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
});
}
// 确保任务执行完成
while (!threadPool.isTerminated()) {
}
}
}
需注意与 SingleThreadExecutor 单个线程的线程池的比较。
可以看到,任务的执行顺序并不是确定的,因为这是抢占式的线程池,哪个任务抢到,哪个任务先执行。
ThreadPoolExecutor():这是最原始,也是最推荐的手动创建线程池的方法。创建时支持自定义某些属性,比如核心线程数、最大线程数等。
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
});
}
}
}
创建一个具有 10 个核心线程、最大线程数为 10 的线程池。具体可设置的参数请参考:线程池七大参数_文丑颜不良啊的博客-CSDN博客
本文参考自:Java 中线程池的 7 种创建方式! - 掘金