线程池基础使用

文章介绍了Java中使用线程池进行线程管理的重要性,展示了如何配置ThreadPoolTaskExecutor,包括设置核心线程数、最大线程数、空闲时间、工作队列和拒绝策略等核心参数。并详细解释了线程池的七大核心参数以及各种拒绝策略的效果。示例代码演示了线程池在实际应用中的使用。
摘要由CSDN通过智能技术生成

为了避免线程重复开销和方便线程管理,在开发中应该尽量避免手动创建线程,使用线程池可以规范管理线程。

    @Bean("taskExecutor")
    public Executor asyncThread(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //设置核心线程数
        executor.setCorePoolSize(5);
        //设置最大线程数
        executor.setMaxPoolSize(100);
        //线程空闲等待时间
        executor.setKeepAliveSeconds(100);
        //设置线程的最大缓存队列
        executor.setQueueCapacity(1000);

        /**
         * 设置拒绝策略
         * rejectedExectutionHandler参数字段用于配置绝策略,常用拒绝策略如下
         *
         * AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException
         *
         * CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。
         *
         * DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。
         *
         * DiscardPolicy:用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        //设置线程组名
        executor.setThreadGroupName("async-thread");
        return executor;
    }

线程池的七大核心参数:
1:核心线程数
2:最大线程数
3:空闲等待时间
4:空闲等待时间单位
5:工作队列
6:线程工厂
7:拒绝策略

一、 corePoolSize 线程池核心线程大小
corePoolSize 是线程池中的一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。

二、maximumPoolSize 线程池最大线程数量
线程池能够容纳同时执行的最大线程数,此值大于等于1。一个任务被提交到线程池以后,首先会找有没有空闲并且存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会放到工作队列中,直到工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。工作队列满,且线程数等于最大线程数,此时再提交任务则会调用拒绝策略。

三、keepAliveTime 多余的空闲线程存活时间
当线程空闲时间达到keepAliveTime值时,多余的线程会被销毁直到只剩下corePoolSize个线程为止。默认情况下:只有当线程池中的线程数大于corePoolSize时keepAliveTime才会起作用,直到线程中的线程数不大于corepoolSIze,

四、unit 空闲线程存活时间单位
keepAliveTime的计量单位

五、workQueue 工作队列
任务被提交给线程池时,会先进入工作队列,任务调度时再从工作队列中取出。
常用工作队列有以下几种

  1. ArrayBlockingQueue(数组的有界阻塞队列)
    ArrayBlockingQueue 在创建时必须设置大小,按FIFO排序(先进先出)。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

  2. LinkedBlockingQueue(链表的无界阻塞队列)
    按 FIFO 排序任务,可以设置容量(有界队列),不设置容量则默认使用 Integer.Max_VALUE 作为容量 (无界队列)。该队列的吞吐量高于 ArrayBlockingQueue。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。有两个快捷创建线程池的工厂方法 Executors.newSingleThreadExecutor、Executors.newFixedThreadPool,使用了这个队列,并且都没有设置容量(无界队列)。

3.SynchronousQueue(一个不缓存任务的阻塞队列)
生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
其 吞 吐 量 通 常 高 于LinkedBlockingQueue。 快捷工厂方法 Executors.newCachedThreadPool 所创建的线程池使用此队列。与前面的队列相比,这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

  1. PriorityBlockingQueue(具有优先级的无界阻塞队列)
    优先级通过参数Comparator实现。

  2. DelayQueue(这是一个无界阻塞延迟队列)
    底层基于 PriorityBlockingQueue 实现的,队列中每个元素都有过期时间,当从队列获取元素(元素出队)时,只有已经过期的元素才会出队,而队列头部的元素是过期最快的元素。快捷工厂方法 Executors.newScheduledThreadPool 所创建的线程池使用此队列。

Java 中的阻塞队列(BlockingQueue)与普通队列相比,有一个重要的特点:在阻塞队列为空时,会阻塞当前线程的元素获取操作。具体来说,在一个线程从一个空的阻塞队列中取元素时,线程会被阻塞,直到阻塞队列中有了元素;当队列中有元素后,被阻塞的线程会自动被唤醒(唤醒过程不需要用户程序干预)。

六、threadFactory 线程工厂
创建一个线程工厂用来创建线程,可以用来设定线程名、是否为daemon线程等等

七、handler 拒绝策略
AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常。(默认这种)
DiscardPolicy:丢弃任务,但是不抛出异常
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) 。也就是当任务被拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务从队尾添加进去,等待执行。
CallerRunsPolicy:谁调用,谁处理。由调用线程(即提交任务给线程池的线程)处理该任务,如果线程池已经被shutdown则直接丢弃。

一个线程池的使用例子:

 ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        FutureTask<OssPicResp> ossPicRespFuture = new FutureTask<>(() -> {
            RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);//设置子线程共享
            return userService.ossUpload(photo, OcrLiveConstant.OSS_ID_CARD_POSITIVE_IMG_SPLICING, super.getMerchantNo(), super.getUserId());
        });
        FutureTask<TongDunOCRResponseEntity> tongDunOCRResponseEntityFutureTask = new FutureTask<>(() -> {
            RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);//设置子线程共享
            return userAuthService.ocrAdvance(super.getUserId(), super.getMerchantNo(), photo);
        });
        GwOssPicResp gwOssPicResp = new GwOssPicResp();
        try {
            ocrRespTaskExecutor.execute(ossPicRespFuture, 10000);
            ocrRespTaskExecutor.execute(tongDunOCRResponseEntityFutureTask, 10000);
            
            //FutureTask的get方式是一个阻塞方法。
            gwOssPicResp.setPhotoName(ossPicRespFuture.get().getPhotoName());
            gwOssPicResp.setUrl(ossPicRespFuture.get().getUrl());
            gwOssPicResp.setTongDunOCRResponseEntity(tongDunOCRResponseEntityFutureTask.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            throw new ServiceException(Messages.getByCode("default.response.errors.system"));
        }
        return BaseResponse.ok(gwOssPicResp);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值