一、什么是线程池
一种
线程
使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。(摘抄自百度百科
什么是线程池
)
二、线程池的优点
1、降低资源消耗;通过重复利用已创建的线程降低创建和销毁造成的消耗。
2、提高响应速度;任务到达时,不用等待线程创建便能立刻执行。
3、提高线程的可管理性;统一监控、管理、调优。
三、四种线程池的创建方式
1、newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
cachedThreadPool.execute(() -> {
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
});
}
}
结果打印:
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-5
Thread.currentThread().getName() = pool-1-thread-3
Thread.currentThread().getName() = pool-1-thread-4
Thread.currentThread().getName() = pool-1-thread-2
Thread.currentThread().getName() = pool-1-thread-9
Thread.currentThread().getName() = pool-1-thread-8
Thread.currentThread().getName() = pool-1-thread-7
Thread.currentThread().getName() = pool-1-thread-6
Thread.currentThread().getName() = pool-1-thread-10
2、newSingleThreadExecutor
创建一个单线程化的线程池,保证所有任务按照指定的顺序执行(FIFO,LIFO,优先级),当要求进程限制时,可以进行使用。
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(() -> {
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
});
}
结果打印:
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
3、newFixedThreadPool
创建一个固定线程数量,可重用的线程池。
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(() -> {
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
});
}
}
结果打印:
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-2
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-2
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-2
4、newScheduledThreadPool
创建一个可定期或者延时执行任务的线程池。
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("延迟5秒后执行");
}
},5, TimeUnit.SECONDS);
scheduledThreadPool.scheduleAtFixedRate(() -> {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("延迟3秒后,每5秒钟执行一次,当前时间:" + LocalDateTime.now().format(dateTimeFormatter));
},3,5,TimeUnit.SECONDS);
}
结果打印:
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:21:58
延迟5秒后执行
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:03
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:08
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:13
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:18
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:23
……
四、手动创建线程池
根据《阿里巴巴Java开发手册》中提到
所以我们在项目开发中,通过ThreadPoolExecutor 来手动创建线程池。那如何手动创建?
1、创建线程池工厂类
@Slf4j
public class ThreadPoolUtil {
/** 核心线程池默认大小 */
private static final Integer DEFAULT_CORE_POOL_SIZE = 8;
/** 默认最大线程数 */
private static final Integer DEFAULT_MAX_POOL_SIZE = 64;
/** 默认空闲线程存活时间(s) */
private static final Integer DEFAULT_FREE_THREAD_KEEP_ALIVE = 60;
/** 默认任务队列大小 */
private static final Integer DEFAULT_TASK_QUEUE_SIZE = 2000;
/** 默认的线程池饱和时任务拒绝策略 */
private static final RejectedExecutionHandler DEFAULT_REJECTED_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy();
/** 线程池对象 */
private ThreadPoolExecutor EXECUTOR;
/** 工厂实例 */
private static volatile ThreadPoolUtil INSTANCE;
/** 线程名称前缀 **/
private static final String THREAD_POOL_PRE_NAME = "XX业务_pool";
private ThreadPoolUtil(Integer coolPoolSize, Integer maxPoolSize, Integer keepAliveTime, TimeUnit timeUnit,
Integer taskQueueSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
this.EXECUTOR = new ThreadPoolExecutor(coolPoolSize, maxPoolSize, keepAliveTime, timeUnit,
new LinkedBlockingQueue<>(taskQueueSize), handler);
this.executor.allowCoreThreadTimeOut(true);
}
/**
* 获取executor对象
* @return
*/
public static ThreadPoolExecutor getExecutor() {
return getInstance().executor();
}
/**
* 线程安全创建线程池对象
* @return FlowWorkerPoolFactory
*/
private static ThreadPoolUtil getInstance() {
if (null == INSTANCE) {
synchronized (ThreadPoolUtil.class) {
if (null == INSTANCE) {
INSTANCE = new ThreadPoolUtil(DEFAULT_CORE_POOL_SIZE, DEFAULT_MAX_POOL_SIZE, DEFAULT_FREE_THREAD_KEEP_ALIVE,
TimeUnit.SECONDS, DEFAULT_TASK_QUEUE_SIZE,
new CustomizableThreadFactory(THREAD_POOL_PRE_NAME), DEFAULT_REJECTED_HANDLER);
}
}
}
return INSTANCE;
}
/**
* 获取executor对象
* @return
*/
private ThreadPoolExecutor executor() {
return this.EXECUTOR;
}
}
2、执行任务
ThreadPoolUtil.getExecutor().execute(() -> getUserName(id));
五、线程池的七个核心参数
参数 | 说明 |
corePoolSize | 线程池核心线程大小 |
maxiPoolSize | 线程池最大线程数量 |
keepAliveTime | 空闲线程存活时间 |
unit | 时间格式 , keepAliveTime 参数的时间单位 |
workQueue |
工作队列(1.ArrayBlocking 2.LinkedBlockingQueue 3.SynchronizedQueue 4.PriorityBlockingQueue)
更多详情可看: |
ThreadFactory | 线程工厂 |
Handler | 拒绝策略 |
其中拒绝策略分为四种:
-
AbortPolicy:中断抛出异常
【默认策略】
- DiscardPolicy:默默丢弃任务,不进行任何通知
- DiscardOldestPolicy:丢弃掉在队列中存在时间最久的任务
- CallerRunsPolicy:让提交任务的线程去执行任务
除上面四种之外,还可以自定义拒绝策略:
/** 自定义个拒绝策略 **/
private static final RejectedExecutionHandler DEFAULT_REJECTED_HANDLER = new LogPolicy();
public static class LogPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.error("threadNamePrefix {}, corePoolSize {}, maxPoolSize {}, workQueueSize {}, rejected task {}.", new Object[]{THREAD_POOL_PRE_NAME, executor.getCorePoolSize(), executor.getMaximumPoolSize(), executor.getQueue().size(), r.toString()});
}
}
六、线程池的执行流程