深入浅出线程池创建和使用

2023-12-04 11:13:40 浏览数 (1)

一、线程池

提供了一个线程对列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高响应的速度。

二、线程池的体系结构:

java.util.concurrent.Executor:负责线程的使用与调度的根接口

    ExecutorService 子接口:线程池的主要接口

        ThreadPoolExecutor 线程池的实现类

        ScheduledExecutorService 子接口

            ScheduledThreadPoolExecutor:继承ThreadPoolExecutor,实现ScheduledExecutorService

三、工具类:Executors

ExecutorService Executors.newFixedThreadPool :创建固定大小的线程池

ExecutorService Executors.newCachedThreadPool():缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量

ExecutorService Executors.newSingleThreadExecutor():创建单个线程池。线程池中只有一个线程

ScheduledExecutorService Executors.newSingleThreadScheduledExecutor() :创建固定大小的线程,可以延迟或定时的执行任务

四、使用ThreadPoolExecutor自定义创建多线程

ThreadPoolExecutor构造函数

(1)5个基本参数

corePoolSize: 核心池的大小, 或者说线程池维护线程的最少数量 (core : 核心)

maximumPoolSize: 线程池中线程的最大数量

keepAliveTime: 线程池维护线程所允许的空闲时间

unit: 线程池维护线程所允许的空闲时间的单位

workQueue: 线程池所使用的缓冲队列

代码语言:javascript复制
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

(1)2个可选参数

handler: 线程池对拒绝任务的处理策略

threadFactory:自定义线程工厂,可以使用om.google.guava ThreadFactoryBuilder

    指定线程名称

    ThreadFactory threadFactory = new ThreadFactoryBuilder()

    .setNameFormat("thread-%d")

    .build();

eg:

代码语言:javascript复制
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
        4,
        4,
        0L,
        TimeUnit.MILLISECONDS,
        new LinkedBlockingDeque<>(10),
        threadFactory,// 线程工厂可以不用传参
        new ThreadPoolExecutor.CallerRunsPolicy()//线程拒绝策略,也可以不用传参
);

五:线程池ThreadPoolExecutor中execute和submit方法对比

(1)定义方法的类不同

submit是在ExecutorService接口中定义的,而execute方法是在Executor类中定义的,ThreadPoolExecutor实现。

execute:

代码语言:javascript复制
public interface Executor {
    void execute(Runnable command);
}

submit

// ExecutorService接口中定义的方法:

代码语言:javascript复制
Future submit(Runnable task);
 Future submit(Runnable task, T result);
 Future submit(Callable task);

(2)返回值类型不同

execute方法返回值为空,submit方法会以Future的形式返回线程的执行结果。

(3)对异常的处理方式不同

如果执行的任务中产生了异常,execute()方法会直接打印产生的异常的堆栈,由于该异常是在子线程中产生,主线程中包围在execute方法周围的try-catch语句不能捕获异常。

而submit提交的子线程如果产生了异常,当调用submit方法返回的Future实例的get方法时,可以在主线程try-catch捕获异常。如果不调用get方法就不能捕获异常

项目应用(使用场景):

1)并发量大的场景,批量查询,提高。多线程(线程池)分配处理

代码语言:javascript复制
//List分批处理
List> productList = Lists.partition(activityProductBatchSaveDTO.getProductIds(), 15);
long time = System.currentTimeMillis();
// 
Map productFullsById = new ConcurrentHashMap<>();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(productList.size(),productList.size(),1, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(100000),
        new ThreadFactoryBuilder().setNameFormat("OMG-Tool-%d").build(),
        new ThreadPoolExecutor.CallerRunsPolicy());
CountDownLatch countDownLatch =new CountDownLatch(productList.size());
productList.forEach(productIds->{
    threadPoolExecutor.execute(()->{
        ProductIdRequest request = BeanMapper.map(req, ProductIdRequest.class);
        request.setProductIds(productIds);
        productFullsById.putAll(productFacade.getProductsByIdsV3(request));
        countDownLatch.countDown();
    });
});
try {
    countDownLatch.await();
}catch (Exception e){
}finally {
    threadPoolExecutor.shutdown();
}

2)耗时较长的任务。比如用户注册之后发送邮件、短信之类的操作,这种任务就算失败了影响也不大,可以使用异步线程。

3)定时任务,比如定期更新配置文件、备份数据之类的任务。

总结

        利用线程池,可以动态配置线程池相关参数,包括核心线程池,非核心线程池,当内存紧张的时候,就可以释放部分线程池,或者直接关闭非核心线程池,并具有如下优点:

(1)降低资源销毁。重用存在的线程,减少对象创建销毁的开销

(2)提高线程管理性。可有效的控制最大并发线程数、提高系统资源的使用率,同时避免过多资源竞争,避免阻塞

(3)提高响应速度,任务到达可以不用创建线程就可以执行。提供定时执行、定期执行、单线程、并发数控制等功能

我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!

0 人点赞