线程池的创建---ThreadPoolExecutor

作者: H_Man | 来源:发表于2017-10-16 14:57 被阅读268次

    今天刚下载上阿里的java规范插件,扫描完成之后发现之前创建线程池的方式有问题.

    旧的
    service = Executors.newFixedThreadPool(poolSize);
    

    发现会提示错误,查看原因发现

    image.png

    线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:

    • newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
    • newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
    线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为: 
    
    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, 
    RejectedExecutionHandler handler) 
    
    • corePoolSize: 线程池维护线程的最少数量
    • maximumPoolSize:线程池维护线程的最大数量
    • keepAliveTime: 线程池维护线程所允许的空闲时间
    • unit: 线程池维护线程所允许的空闲时间的单位
    • workQueue: 线程池所使用的缓冲队列
    • handler: 线程池对拒绝任务的处理策略

    一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

    当一个任务通过execute(Runnable)方法欲添加到线程池时:

    如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
    如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
    如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
    如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

    也就是:处理任务的优先级为:
    核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

    当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

    unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
    NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。

    workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue

    handler有四个选择:

    • ThreadPoolExecutor.AbortPolicy()
      抛出java.util.concurrent.RejectedExecutionException异常
    • ThreadPoolExecutor.CallerRunsPolicy()
      重试添加当前的任务,他会自动重复调用execute()方法
    • ThreadPoolExecutor.DiscardOldestPolicy()
      抛弃旧的任务
    • ThreadPoolExecutor.DiscardPolicy()
      抛弃当前的任务

    推荐示例1

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

    推荐示例2

    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
           
    

    推荐示例3

    <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);
    

    相关文章

      网友评论

      • George_Antonio:看Executors的源码可以发现,Executors.newFixedThreadPool(poolSize)方法在底层也是调用的ThreadPoolExecutor创建的
        H_Man:@H_Man new ThreadPoolExecutor(nThreads, nThreads,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>()); Executors内部的源码是默认keepAliveTime,个人认为灵活性不是很够
        H_Man: @M_Sir 是吗 这我还真没发现,受教了,下次认真一点

      本文标题:线程池的创建---ThreadPoolExecutor

      本文链接:https://www.haomeiwen.com/subject/caikuxtx.html