线程池顾名思义是为线程的池子; 在Java的世界里, 我们都知道所有应用程序都是运行在JVM运行实例之上, 每个JVM实例对应到操作系统中就是一个个独立的系统进程; 但是往往我们的应用中需要有异步, 并发, 并行执行程序的需求, 操作系统为此提供支持, 在操作系统调度层面的最小调度单元刚好是线程; 但是因为线程的创建和销毁都是比较耗费资源的, 因此在程序中我们需要线程池这样的协调者帮我们做线程对象的创建,管理,重复利用等事情,以提高程序的运行效率;
线程池的常见用法
Executors 工具类创建常见的线程池执行器
package org.hinsteny.jvm.threadpool.executors;
import java.util.concurrent.*;
/**
* Executors examples
* @author Hinsteny
* @version $ID: ExecutorsExample 2018-09-17 23:30 All rights reserved.$
*/
public class ExecutorsExample {
/**
* 测试方法
*
* @param args
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
testSingleThreadExecutor(10);
testnewSingleThreadScheduledExecutor(10);
testFixedThreadPool(12);
testCachedThreadPool(30);
testWorkStealingPool(100000);
}
/**
* 创建只有一个执行任务线程的线程池, 所有丢进线程池的任务会在一个无限缓存队列中排序, 然后依次按序执行;
*
* @param count
*/
private static void testSingleThreadExecutor(int count) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int x = 0; x < count; x++) {
final int taskId = x + 1;
executorService.submit(() ->
System.out.println(String.format("Task [%s] execute by thread [%s]", taskId, Thread.currentThread().getId()))
);
}
executorService.shutdown();
}
/**
* 创建只有一个执行任务线程的线程池, 所有丢进线程池的任务会在一个无限缓存队列中排序, 然后依次按序执行;
*
* @param count
*/
private static void testnewSingleThreadScheduledExecutor(int count) throws ExecutionException, InterruptedException {
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r));
for (int x = 0; x < count; x++) {
final int taskId = x + 1;
ScheduledFuture<Long> future = scheduledExecutorService.schedule(() -> {
long start = System.currentTimeMillis();
System.out.println(String.format("Task [%s] execute by thread [%s]", taskId, Thread.currentThread().getId()));
return System.currentTimeMillis() - start;
}
, 3, TimeUnit.SECONDS);
System.out.println(String.format("Task [%s] execute result [%s]", taskId, future.get()));
}
scheduledExecutorService.shutdown();
}
/**
* 创建一个固定大小的线程池, 来新任务时, 有空闲线程对象则立即执行, 无空闲线程时则默认先放在队列中
* @param count
*/
private static void testFixedThreadPool(int count) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int x = 0; x < count; x++) {
final int taskId = x + 1;
executorService.submit(() ->
System.out.println(String.format("Task [%s] execute by thread [%s]", taskId, Thread.currentThread().getId()))
);
}
executorService.shutdown();
}
/**
* 创建无大小限制的线程池, 来一个线程任务就创建一个新线程, 但是池中有可用的已回收线程对象则优先使用不创建新线程
* @param count
* @throws InterruptedException
*/
private static void testCachedThreadPool(int count) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int x = 0; x < count; x++) {
final int taskId = x + 1;
executorService.submit(() ->
System.out.println(String.format("Task [%s] execute by thread [%s]", taskId, Thread.currentThread().getId()))
);
if (x % 10 == 9) {
Thread.sleep(1000);
}
}
executorService.shutdown();
}
/**
* 创建一个工作窃取的线程池
* 针对一个提交到线程池的任务, 在它的执行过程中, 会分化裂变出多个可并行执行的子任务, 这时可以充分利用多处理器的功能, 并行计算, 最后汇总结果返回;
*/
private static void testWorkStealingPool(int max) throws ExecutionException, InterruptedException {
ForkJoinPool executorService = (ForkJoinPool) Executors.newWorkStealingPool(5);
ForkJoinTask<Long> result = executorService.submit(new NumberSumTask(0, max, max / 100));
while (true) {
if (result.isDone()) {
System.out.println(String.format("NumberSumTask start with: %d, end at: %d, result is: %d", 0, max, result.get()));
break;
}
}
executorService.shutdown();
}
/**
* 二分法递归任务拆解的方式对一个有序自增1的整数序列求和的计算任务
*/
private static class NumberSumTask extends RecursiveTask<Long>{
private int start;
private int end;
private int threadHold;
public NumberSumTask(int start, int end, int threadHold) {
this.start = start;
this.end = end;
this.threadHold = threadHold;
}
@Override
protected Long compute() {
// 判断数值区间长度是否小于给定的单个线程计算量, 小于则直接计算, 否则继续拆解
if (end - start < threadHold) {
System.out.println(String.format("SubTask sum start with: %d, end at: %d, by thread: %d", start, end, Thread.currentThread().getId()));
long sum = 0;
for (; start < end + 1; start ++) {
sum += start;
}
return sum;
} else {
// 分割任务
int middle = (start + end) / 2;
NumberSumTask leftTask = new NumberSumTask(start, middle, threadHold);
NumberSumTask rightTask = new NumberSumTask(middle, end, threadHold);
// 异步执行 leftTask
leftTask.fork();
// 异步执行 rightTask
rightTask.fork();
// 阻塞,直到 leftTask 执行完毕返回结果
Long leftResult = leftTask.join();
// 阻塞,直到 rightTask 执行完毕返回结果
Long rightResult = rightTask.join();
// 合并结果
return leftResult + rightResult;
}
}
}
}
自定义按需创建线程池对象
通过直接创建ThreadPoolExecutor类的实例来生成线程池对象;
ThreadPoolExecutor的构造参数介绍
ThreadPoolExecutor: 用来自定义创建线程池的基础类, 在Executors里面创建各种线程池的方法内部也是调用的该类, 它具有以下构造参数;
- int corePoolSize: 核心线程数 -> 工作线程存活的个数, 默认空闲也不会被回收, 如果设置allowCoreThreadTimeOut=true, 那就会回收核心线程, 即核心线程数为0;
- int maximumPoolSize: 最大线程数 -> 最大允许创建的线程池中数目,
- long keepAliveTime: 存活时间 -> 非核心的空闲线程在超过此存活时间都没有被安排执行新任务就会被回收;
- TimeUnit unit: 存活时间单位 ->
- BlockingQueue workQueue: 任务队列 -> 当任务提交到线程池后, 如果没有可用的核心线程, 就会被丢到此队列, 等核心线程空闲, 再从队列中拿出来执行;
- ThreadFactory threadFactory: 线程工厂 -> 创建新线程对象的工厂
- RejectedExecutionHandler handler: 拒绝策略 -> 当线程池不能接收处理新的task时, 所采用的拒绝策略;
BlockingQueue 列表
- LinkedBlockingQueue: 阻塞链表队列 -> 可以指定大小的先进先出的链表队列, 尾部添加入队, 头部出队移出; 相比于数组队列, 链表队列拥有较高吞吐量, 但是在高并发环境下性能差一点;
- LinkedBlockingDeque: 阻塞链表双端队列 ->
- ArrayBlockingQueue: 阻塞数组队列 -> 初始指定大小的先进先出队列, 尾部添加入队, 头部出队移出; 从对头遍历; 满队列插入或空队列取出都会阻塞;
- LinkedTransferQueue: 顺序链表队列 -> 不限大小, 先进先出的链表队列;
- PriorityBlockingQueue: 阻塞优先队列 ->
- SynchronousQueue: 同步操作队列 ->
- DelayQueue: 延迟队列 ->
- DelayedWorkQueue: 延迟工作队列 ->
ThreadFactory 列表
- DefaultThreadFactory:
- PrivilegedThreadFactory:
RejectedExecutionHandler 列表
- AbortPolicy: 直接抛出异常RejectedExecutionException
- CallerRunsPolicy: 如果线程池没关闭, 就直接同步调用run方法执行任务;
- DiscardOldestPolicy: 丢弃任务队列头部的一个老任务, 把新任务添加到任务队列中;
- DiscardPolicy: 直接丢弃新任务, 不报错;
代码示例
自定义参数创建一个普通的线程池, 并提交任务
/**
* 自定创建一个普通的线程池对象, 进行任务的提交执行
*/
private static void newNormalThreadPoolExecutor() throws ExecutionException, InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), Executors.defaultThreadFactory(), new AbortPolicy());
threadPoolExecutor.execute(() -> System.out.println("execute one task!"));
Future<Integer> future = threadPoolExecutor.submit(() -> {
System.out.println("submit and execute one task!");
Thread.sleep(3000);
return 1;
});
// Future<Integer> future.get() 此方法会阻塞, 直到任务执行完成产生返回结果
System.out.println(String.format("submit task result is: %s!", future.get()));
threadPoolExecutor.shutdown();
}
自定义创建线程池, 了解线程任务提交处理的逻辑
/**
* 创建一个
* 核心线程为1
* 最大线程数2
* 阻塞队列, 长度为2
* 线程存活线程时间3s
* 拒绝策略为抛异常
* 单个任务执行耗时1s
* 线程池的执行逻辑: 向线程池中提交任务, 当其中的工作线程达到最大线程池个数时, 任务就会被放到阻塞队列中, 当队列也满了时, 再提交任务, 就会执行我们指定的拒绝策略;
*
*/
private static void newThreadPoolWithLinkedBlockingDeque() throws InterruptedException {
// 构建参数
int corePoolSize = 1;
int maxPollSize = 2;
long keepAliveTime = 3;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue(2);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler abortPolicy = new AbortPolicy();
// 构建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPollSize, keepAliveTime, unit, workQueue, threadFactory, abortPolicy);
// 任务执行结果统计
final AtomicInteger total = new AtomicInteger(0);
final AtomicInteger success = new AtomicInteger(0);
final AtomicInteger failed = new AtomicInteger(0);
// 提交任务
for (int i = 1; i <= 12; i++) {
final String taskId = String.valueOf(i);
System.out.println(String.format("submit one task: %s ", taskId));
try {
total.getAndIncrement();
threadPoolExecutor.execute(() -> {
System.out.println(String.format("==================================== execute task start: %s !", taskId));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
success.getAndIncrement();
System.out.println(String.format("+++++++++++++++++++++++++++++++++++ execute task finished: %s , success: %d!", taskId, success.get()));
});
} catch (Throwable e) {
failed.getAndIncrement();
new Throwable("Task: " + taskId + " executed exception", e).printStackTrace();
}
if (i % 5 == 0) {
Thread.sleep(1000);
}
}
threadPoolExecutor.shutdown();
// 线程池关闭了, 但是其中的任务并没有都执行完成
// while (!threadPoolExecutor.isShutdown()) {
// 线程池关闭了, 并且其中的任务都全部执行完成啦
while (!threadPoolExecutor.awaitTermination(1, TimeUnit.MINUTES)) {
Thread.sleep(1000);
}
System.out.println(String.format("Submit tasks total: %d, executed success is: %d and exception is: %d !", total.get(), success.get(), failed.get()));
}