1 摘要
Java 线程池在批量处理的场景中应用广泛。常见的四种创建线程池的方法:newSingleThreadExecutor,newFixedThreadPool,newCachedThreadPool,newScheduledThreadPool,各有各的缺陷,不建议使用。本文将给出根据阿里巴巴Java开发手册推荐的方式创建线程池的示例。
2 推荐创建线程池的方法
java.util.concurrent.ThreadPoolExecutor
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数说明:
corePoolSize: 线程池核心线程数量,推荐为服务器的核心数量
maximumPoolSize: 线程池最大线程数量,推荐为和服务核心数量的两倍
keepAliveTime: 线程销毁等待时间,当线程数超过核心线程数量(corePoolSize)时,销毁多余线程需要等待的最大时间;可根据任务执行的频率设置时间长度
unit: 线程销毁等待时间单位,有毫秒,秒,小时等
workQueue: 工作负载阻塞队列,当线程池中线程数量达到最大值时,仍然有未处理的任务,此时会将任务放在该队列中
threadFactory: 线程工厂,用于创建线程
RejectedExecutionHandler: 线程池拒绝策略,当线程数量达到线程池最大值,工作负载队列满了之后,线程池对于新任务的处理策略。有四种:
java.util.concurrent.ThreadPoolExecutor.AbortPolicy: 抛出异常,不作处理
java.util.concurrent.ThreadPoolExecutor.DiscardPolicy: 丢弃新任务,也不抛出异常
java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy: 丢弃最老的任务
java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy: 把任务交给调用线程池的线程处理,直到线程池死了
通常情况下选择抛出异常的策略即可。
3 核心示例代码
3.1 任务负载
./src/main/java/com/ljq/demo/concurrent/DrinkTask.java
package com.ljq.demo.concurrent;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
/**
* @Description: 喝酒任务
* @Author: junqiang.lu
* @Date: 2021/5/14
*/
@Slf4j
public class DrinkTask implements Callable<Integer> {
/**
* 当前杯数
*/
private int cupNumber;
public DrinkTask(int cupNumber) {
this.cupNumber = cupNumber;
}
@Override
public Integer call() throws Exception {
log.info("武松喝完了第[" + cupNumber + "]杯酒");
Thread.sleep(100);
return cupNumber;
}
}
3.2 简易线程池工具类
./src/main/java/com/ljq/demo/util/ThreadPoolUtil.java
package com.ljq.demo.util;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* @Description: 线程池工具类
* @Author: junqiang.lu
* @Date: 2021/5/14
*/
public class ThreadPoolUtil {
private ThreadPoolUtil(){
}
static {
initExecutor();
}
/**
* 线程池执行器
*/
private static transient ThreadPoolExecutor executor;
/**
* 执行任务
* (不包含返回值)
*
* @param command
*/
public static void execute(Runnable command) {
executor.execute(command);
}
/**
* 执行任务
* (包含返回值)
*
* @param task
* @param <T>
* @return
*/
public static <T> Future<T> executeAsync(Callable<T> task) {
return executor.submit(task);
}
/**
* 销毁线程池
*/
public static void shutdown() {
executor.shutdown();
}
/**
* 初始化线程池
*/
private static void initExecutor() {
if (executor == null || executor.isShutdown()) {
synchronized (ThreadPoolUtil.class) {
if (executor == null || executor.isShutdown()) {
executor = new ThreadPoolExecutor(4,10,100,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000),
ThreadFactoryBuilder.create().build(), new ThreadPoolExecutor.AbortPolicy());
}
}
}
}
}
3.3 线程池运行测试示例
./src/main/java/com/ljq/demo/concurrent/DrinkThreadPoolDemo.java
package com.ljq.demo.concurrent;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.thread.ThreadUtil;
import com.ljq.demo.util.ThreadPoolUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Description: 喝酒线程池示例
* @Author: junqiang.lu
* @Date: 2021/5/14
*/
@Slf4j
public class DrinkThreadPoolDemo {
public static void main(String[] args) {
manualThreadPool();
huToolThreadUtil();
localThreadPoolUtil();
}
/**
* 手动创建线程池
*/
public static void manualThreadPool() {
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,10,100,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000), ThreadFactoryBuilder.create().build(),
new ThreadPoolExecutor.AbortPolicy());
// 总杯数
int totalCup = 100;
int cupNumber;
for (int i = 1; i <= totalCup; i++) {
try {
cupNumber = executor.submit(new DrinkTask(i)).get();
log.info("manual cupNumber: {}", cupNumber);
} catch (InterruptedException e) {
log.error("线程池执行错误", e);
} catch (ExecutionException e) {
log.error("武松喝醉了", e);
}
}
// 关闭线程池
executor.shutdown();
}
/**
* 使用 hutool 线程工具类
* (线程池不会自动销毁)
*/
public static void huToolThreadUtil() {
// 总杯数
int totalCup = 100;
int cupNumber;
for (int i = 1; i <= totalCup; i++) {
try {
cupNumber = ThreadUtil.execAsync(new DrinkTask(i)).get();
log.info("hutool cupNumber: {}", cupNumber);
} catch (InterruptedException e) {
log.error("线程池执行错误", e);
} catch (ExecutionException e) {
log.error("武松喝醉了", e);
}
}
}
/**
* 本地线程池工具类
*/
public static void localThreadPoolUtil() {
// 总杯数
int totalCup = 100;
int cupNumber;
for (int i = 1; i <= totalCup; i++) {
try {
cupNumber = ThreadPoolUtil.executeAsync(new DrinkTask(i)).get();
log.info("local cupNumber: {}", cupNumber);
} catch (InterruptedException e) {
log.error("线程池执行错误", e);
} catch (ExecutionException e) {
log.error("武松喝醉了", e);
}
}
ThreadPoolUtil.shutdown();
}
}
3.4 部分执行日志
2021-05-14 16:50:58:890 [pool-4-thread-1] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[1]杯酒
2021-05-14 16:50:58:992 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 1
2021-05-14 16:50:58:993 [pool-4-thread-2] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[2]杯酒
2021-05-14 16:50:59:094 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 2
2021-05-14 16:50:59:095 [pool-4-thread-3] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[3]杯酒
2021-05-14 16:50:59:199 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 3
2021-05-14 16:50:59:200 [pool-4-thread-4] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[4]杯酒
2021-05-14 16:50:59:301 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 4
2021-05-14 16:50:59:301 [pool-4-thread-1] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[5]杯酒
2021-05-14 16:50:59:401 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 5
... ...
2021-05-14 16:51:08:694 [pool-4-thread-4] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[96]杯酒
2021-05-14 16:51:08:794 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 96
2021-05-14 16:51:08:795 [pool-4-thread-1] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[97]杯酒
2021-05-14 16:51:08:897 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 97
2021-05-14 16:51:08:897 [pool-4-thread-2] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[98]杯酒
2021-05-14 16:51:08:998 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 98
2021-05-14 16:51:08:999 [pool-4-thread-3] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[99]杯酒
2021-05-14 16:51:09:101 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 99
2021-05-14 16:51:09:102 [pool-4-thread-4] INFO com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[100]杯酒
2021-05-14 16:51:09:203 [main] INFO com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 100
4 推荐参考示例
5 Github 源码
Gtihub 源码地址 : https://github.com/Flying9001/Demo
个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.