1-自定义线程池

本文围绕Java线程池展开,介绍了定制线程池的七个参数,如核心线程数、最大线程数等,还说明了饱和策略。同时阐述了提交任务的两种方法,execute和submit。对于线程池关闭,介绍了shutdown和shutdownNow的原理及适用场景。最后讲解了通过线程池属性进行监控的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java提供了丰富的线程池,但是通常我们需要根据业务需求产生的运行环境定制自己的线程池,我们先来看看如何定制自己的线程池。

 

一、定制自己的线程池

package com.peace.web.test.threadpool;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author x
 * @date 2020/12/3 20:54
 */
@Slf4j
public class TestThreadPool {

    public static void main(String[] args) {

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10, 1000,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>(9000), 
                new CustomizableThreadFactory("spring-threadpool-"));
        threadPool.allowCoreThreadTimeOut(false);
        threadPool.execute(() -> log.info("a"));
        threadPool.shutdown();
    }
}

第一个参数:int corePoolSize:核心线程数,在线程池线程数量没有达到corePoolSize时,线程数量会伴随任务量等量增加,直到达到corePoolSize,他和初始化时线程数没任何关系;

第二个参数:int maximumPoolSize:当线程数量达到核心线程数量,并且任务队列满了,就会继续创建线程,直到线程数量达到maximum,池中线程数量无论如何不可能超过这个值;

第三个参数:long keepAliveTime:线程存活空闲时间,当线程空闲时间超过这个时间,就可以被销毁,默认只销毁非核心线程数量之外的线程,可通过ThreadPoolExecutor#allowCoreThreadTimeOut(true)方法设置允许销毁核心线程;

第四个参数:TimeUnit unit:keepAliveTime的单位,可设置为小时或分钟等值;

第五个参数:BlockingQueue<Runnable> workQueue:任务队列,是一个阻塞队列(能保证线程安全);

第六个参数:ThreadFactory threadFactory:线程工厂,能够设置线程名字,方便定位生产问题;

第七个参数:RejectedExecutionHandler handler:无法添加任务(队列满,线程数量达到maximum)时的饱和策略,可不传使用默认值。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略:

  1. AbortPolicy:直接抛出异常。
  2. CallerRunsPolicy:只用调用者所在线程来运行任务。
  3. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  4. DiscardPolicy:不处理,丢弃掉。

当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

二、提交任务

我们可以使用execute提交的任务,但是execute方法没有返回值,所以无法判断任务知否被线程池执行成功。我们也可以使用submit 方法来提交任务,它会返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。

threadPool.execute(() -> log.info("a"));

Future<?> b = threadPool.submit(() -> {
    log.info("b");
    return "success";
});
try {
    String ret = (String) b.get();
    log.info("get return:{}", ret);
} catch (InterruptedException | ExecutionException e) {
    log.error("", e);
}
threadPool.shutdown();

三、线程池关闭

我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,但是它们的实现原理不同,shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。

只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。

四、线程池的监控

通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用
taskCount:线程池需要执行的任务数量。
completedTaskCount:线程池在运行过程中已完成的任务数量。小于或等于taskCount。
largestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了。
getPoolSize:线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不减。
getActiveCount:获取活动的线程数。

附录1

第一种 CustomizableThreadFactory

Spring 框架提供的 CustomizableThreadFactory

ThreadFactory springThreadFactory = new CustomizableThreadFactory("springThread-pool-");

ExecutorService exec = new ThreadPoolExecutor(1, 1,
		0L, TimeUnit.MILLISECONDS,
		new LinkedBlockingQueue<Runnable>(10),springThreadFactory);
exec.submit(() -> {
	logger.info("springThreadFactory");
});

第二种 ThreadFactoryBuilder

Google guava 工具类 提供的 ThreadFactoryBuilder ,使用链式方法创建。

ThreadFactory guavaThreadFactory = new ThreadFactoryBuilder().setNameFormat("retryClient-pool-").build();


ExecutorService exec = new ThreadPoolExecutor(1, 1,
		0L, TimeUnit.MILLISECONDS,
		new LinkedBlockingQueue<Runnable>(10),guavaThreadFactory );
exec.submit(() -> {
	logger.info("guavaThreadFactory");
});

第三种 BasicThreadFactory

Apache commons-lang3 提供的 BasicThreadFactory.

ThreadFactory basicThreadFactory = new BasicThreadFactory.Builder()
		.namingPattern("basicThreadFactory-").build();

ExecutorService exec = new ThreadPoolExecutor(1, 1,
		0L, TimeUnit.MILLISECONDS,
		new LinkedBlockingQueue<Runnable>(10),basicThreadFactory );
exec.submit(() -> {
	logger.info("apacheThreadFactory");
});

最终本质都是 给 java.lang.Thread#name 设置名称,详情源码感兴趣的可以自行查看。

final Thread thread = new Thread();
thread.setName(name);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值