java多线程:线程池的使用

以下摘自阿里开发手册原话
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

阿里推荐线程池

  • Positive example 1(commons.lang3):

      //org.apache.commons.lang3.concurrent.BasicThreadFactory
        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
            new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
    
  • Positive example 2(guava):

    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("demo-pool-%d").build();
    
        //Common Thread Pool
        ExecutorService pool = new ThreadPoolExecutor(5, 200,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
    
        pool.execute(()-> System.out.println(Thread.currentThread().getName()));
        pool.shutdown();//gracefully shutdown
    
  • Positive example 3(spring异步任务线程池):

    <bean id="userThreadPool"
            class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
         <property name="corePoolSize" value="10" />
         <property name="maxPoolSize" value="100" />
         <property name="queueCapacity" value="2000" />
    
     <property name="threadFactory" value= threadFactory />
         <property name="rejectedExecutionHandler">
             <ref local="rejectedExecutionHandler" />
         </property>
     </bean>
    //in code
    userThreadPool.execute(thread);
    

自己开发使用

  • commons.lang3
    @Slf4j
    public class ThreadPoolHelper {
    
        private static final int POOL_SIZE = 10;//线程池大小
    
        //任务线程池
    
        private static ThreadPoolExecutor comitTaskPool =(ThreadPoolExecutor) new ScheduledThreadPoolExecutor(POOL_SIZE,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build());
    
    
        /**
         * 执行任务
         * 默认5个线程池执行任务
         * @param comitTask
         */
        public static void executeTask(Runnable comitTask) {
            comitTaskPool.execute(comitTask);
            log.debug("【线程池任务】线程池中线程数:" + comitTaskPool.getPoolSize());
            log.debug("【线程池任务】队列中等待执行的任务数:" + comitTaskPool.getQueue().size());
            log.debug("【线程池任务】已执行完任务数:" + comitTaskPool.getCompletedTaskCount());
        }
    
        /**
         * 指定线程池长度执行线程
         * @param poolSize
         * @param comitTask
         */
        public static void executeTask(int poolSize,Runnable comitTask) {
            comitTaskPool.setCorePoolSize(poolSize);
            executeTask(comitTask);
        }
    
    
        /**
         * 关闭线程池
         */
        public static void shutdown() {
            log.debug("shutdown comitTaskPool...");
            comitTaskPool.shutdown();
            try {
                if (!comitTaskPool.isTerminated()) {
                    log.debug("直接关闭失败[" + comitTaskPool.toString() + "]");
                    comitTaskPool.awaitTermination(3, TimeUnit.SECONDS);
                    if (comitTaskPool.isTerminated()) {
                        log.debug("成功关闭[" + comitTaskPool.toString() + "]");
                    } else {
                        log.debug("[" + comitTaskPool.toString() + "]关闭失败,执行shutdownNow...");
                        if (comitTaskPool.shutdownNow().size() > 0) {
                            log.debug("[" + comitTaskPool.toString() + "]没有关闭成功");
                        } else {
                            log.debug("shutdownNow执行完毕,成功关闭[" + comitTaskPool.toString() + "]");
                        }
                    }
                } else {
                    log.debug("成功关闭[" + comitTaskPool.toString() + "]");
                }
            } catch (InterruptedException e) {
                log.warn("接收到中断请" + comitTaskPool.toString() + "停止操作");
            }
        }
    }
    
  • spring异步任务
  1. 引入依赖开启yml文件自动提示功能

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <!--<optional>true</optional>-->
    </dependency>
    
  2. spring异步任务线程池配置

    import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
    /**
     * @author: hs
     * @Date: 2019/3/18 09:37
     * @Description:
     */
    @Configuration
    @EnableAsync
    @EnableConfigurationProperties(AsyncTaskConfig.AsyncTaskProperties.class)
    public class AsyncTaskConfig {
    
        @Bean
        public AsyncTaskExecutor asyncTaskExecutor(AsyncTaskProperties asyncTaskProperties) {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setThreadNamePrefix(defaultIfBlank(asyncTaskProperties.threadNamePrefix,"invocation-list-"));
            executor.setMaxPoolSize(toInt(asyncTaskProperties.getMaxPoolSize(), 10));
            executor.setCorePoolSize(toInt(asyncTaskProperties.getCorePoolSize(), 5));
            executor.setQueueCapacity(toInt(asyncTaskProperties.getQueueCapacity(), 20));
            //若没有空闲的线程,则把任务抛给主线程去执行
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;
        }
    
        @Data
        @ConfigurationProperties("spring.async")
        public static class AsyncTaskProperties {
            private Integer maxPoolSize;
            private Integer corePoolSize;
            private Integer queueCapacity;
            private String threadNamePrefix;
        }
    
        /**
         * @param num
         * @param defaultNum
         * @return
         */
        private int toInt(Integer num, int defaultNum) {
            return Objects.equal(num, null) ? defaultNum : num;
        }
    }
    
  3. yml文件配置(此配置文件也可以不配置,会自动使用配置类中默认配置)

    spring:
      async:
        core-pool-size: 6
        max-pool-size: 15
        queue-capacity: 50
    
  4. 使用

    @Slf4j
    @Service
    public class ServerCalculateServiceImpl extends JestBaseDao implements ServerCalculateService {
    
        @Async
        @Override
        public void asyncInsertServerCalculates(List<ServerCalculate> list) {
            LocalDate now = LocalDate.now();
            this.batchDocument(list,
                    JestConsts.Index.CALL_LIST.getIndexName().concat(now.toString()),
                    JestConsts.Index.CALL_LIST.getIndexType());
        }
    }
    

    多模块分离情况下,可在启动类上加入如下注解(可以用其他方式,仅供参考):

    /**
     * 异步任务
     * @author hs
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({AsyncTaskConfig.class})
    public @interface EnableAsyncTask {
    
    }
    

猜你喜欢

转载自blog.csdn.net/qq_26869339/article/details/90141656