zoukankan      html  css  js  c++  java
  • Java 自定义线程池的线程工厂

      本文分享创建线程工厂 ThreadFactory 的三种方式,以方便大家快速创建线程池,并通过线程工厂给每个创建出来的线程设置极富业务含义的名字。

    线程池大小考虑因素

      由于需要自定义线程池,故这里先介绍线程池大小如何设定最为合理。我们需要分析计算环境、资源预算和任务的特性,例如,考虑一下在部署的服务器上有多少个CPU,内存是多大,任务是计算密集型、I/O密集型还是二者皆可。

      对于计算密集型的任务,在拥有N(CPU)个处理器的服务器上,当线程池的大小为N+1时,通常能实现最优的利用率。对于包含I/O操作或者其它阻塞操作的任务,由于线程并不会一直执行,因此线程池的规模应该更大,参考值可以设置为2**N(CPU)。线程池资源并不是唯一影响线程池大小的资源,还包括内存、文件句柄、套接字句柄和数据库连接等。在观察任务运行情况和系统负载、资源利用率之后,请酌情调整。

      在Java中,可以使用如下代码获取CPU的数量:

    int N(CPU)= Runtime.getRuntime().availableProcessors();

    基于此可以设置合适的线程池数量,提高系统稳定性和吞吐率。

    Spring CustomizableThreadFactory

      此方式是利用Spring 框架提供的轮子 CustomizableThreadFactory。

    ThreadFactory springFactory = new CustomizableThreadFactory("spring-pool-");
    ExecutorService exec = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(100), springFactory);
    exec.submit(() -> {
        logger.info("--记忆中的颜色是什么颜色---");
    });
    

    guava ThreadFactoryBuilder

      使用Google 开源框架guava提供的 ThreadFactoryBuilder 可以快速给线程池里的线程设置有意义的名字。

    ThreadFactory guavaFactory = new ThreadFactoryBuilder().setNameFormat("guava-pool-").build();
    
    
    ExecutorService exec = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(100),guavaFactory );
    exec.submit(() -> {
        logger.info("--记忆中的颜色是什么颜色---");
    });
    

    此方法需要引入如下guava maven坐标:

         <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>20.0</version>
            </dependency>
    

    Apache commons-lang3 BasicThreadFactory

      Apache commons-lang3 提供的 BasicThreadFactory.

    ThreadFactory basicFactory = new BasicThreadFactory.Builder()
            .namingPattern("basicFactory-pool-").build();
    
    ExecutorService exec = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(100),basicFactory );
    exec.submit(() -> {
        logger.info("--记忆中的颜色是什么颜色---");
    });
    

      使用的列队是无界队列LinkedBlockingQueue。如果当前执行任务数量大于核心线程数,此时再提交任务就在添加到阻塞队列中等待执行,直到有可用线程。温馨提示,无界阻塞队列可能消耗很大的内存资源。值得注意的是,如果使用了无界的任务队列,线程池最大数量这个参数就没什么效果了。

    优雅的自定义线程工厂

      除了使用第三方jar包之外,我们也可以基于ThreadPoolExecutor优雅地自定义ThreadFactory,并根据自己的需要来操作线程,下面是实例代码:

    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.*;
    
    @Slf4j
    public class ThreadPoolStudy {
        public static void main(String[] args) {
            ExecutorService executor = initExecutor();
            // 若i>0,则不会抛出异常信息。设为0是为了验证如何捕捉异常
            for (int i = 0; i < 10; i++) {
                DivTask myTask = new DivTask(100, i);
                Future future = executor.submit(myTask);
                try {
                    // 阻塞方法,尽量不要调用,这里调用get是为了避免任务执行异常被吞掉
                    future.get();
                } catch (Exception e) {
                    log.error("任务执行异常,", e);
                }
            }
            executor.shutdown();
            log.info("线程池马上关闭,不再接收新任务。");
        }
        // 自定义任务,用于求两数相除的结果
        class DivTask implements Runnable {
            int a, b;
    
            public DivTask(int a, int b) {
                this.a = a;
                this.b = b;
            }
    
            @Override
            public void run() {
                log.info("计算结果:{}", a / b);
            }
        }
    
        /**
         * 初始化线程池
         *
         * @return
         */
        private static ExecutorService initExecutor() {
            ThreadFactory guavaFactory = new ThreadFactoryBuilder().setNameFormat("guava-pool-").build();
            ExecutorService executor = new ThreadPoolExecutor(1, 64,
                    30L, TimeUnit.MILLISECONDS,
                    new SynchronousQueue<Runnable>(), guavaFactory);
    
            return executor;
        }
    }
    

      任务执行完毕后,请关闭线程池,及时降低资源损耗。

    小结

      作为程序员,要有“刨根问底”的精神。知其然,更要知其所以然。这篇文章希望能帮助你对自定义线程工厂抽丝剥茧,还原背后的原理。

    Reference


      读后有收获,小礼物走一走,请作者喝咖啡。
      Buy me a coffee. ☕
  • 相关阅读:
    今日总结
    微任务与宏任务
    20171128微信小程序
    20171128-微信小程序之点餐
    git
    第二次学习Javascript笔记
    base64图片
    网页布局基础-css版
    StuQ技能图谱——前端
    前端开发工具
  • 原文地址:https://www.cnblogs.com/east7/p/15678970.html
Copyright © 2011-2022 走看看