必备技能——使用线程池来初始化缓冲队列

在我们的业务场景中,经常会遇到一些需要在系统启动时就需要手动缓存一下线程,方便我们去使用已有的线程去处理一些业务,降低系统资源的消耗等,今天我们就来讲解下Java开发必备技能,使用线程池初始化缓冲队列

ServletListenerRegistrationBean

我们用来演示的项目是基于SpringBoot框架,在框架中我们需要向web容器中注册监听器,可以使用ServletListenerRegistrationBean来实现,具体的代码如下:

    /**
     * Copyright © 2018 五月工作室. All rights reserved.
     *
     * @Package com.amos.common.config
     * @ClassName ServletListenerRegistrationConfig
     * @Description 在容器启动的时候,注册自定义的Listener
     * @Author Amos
     * @Modifier
     * @Date 2019/7/14 16:41
     * @Version 1.0
     **/
    @Configuration
    public class ServletListenerRegistrationConfig {
    
        /**
         * 注册自定义的Bean
         *
         * @return
         */
        @Bean
        public ServletListenerRegistrationBean registrationBean() {
            ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
            servletListenerRegistrationBean.setListener(new InitThreadLocalPoolListen());
            return servletListenerRegistrationBean;
        }
    }

其中InitThreadLocalPoolListen就是我们自定义的监听器,可以在通过该监听器实现我们需要处理的逻辑

线程池

线程池:创建一些线程,使用池化技术来存储这些线程。
使用线程池有如下的好处:

  • 降低资源消耗

可以利用重复已创建的线程降低线程创建和销毁的消耗

  • 提高响应速度

当任务到达时,任务可以不需要等到线程创建就能立即执行

  • 提高线程的可管理性

使用线程池可以进行统一分配、调优和监控

线程池的创建其实说到底就一种实例化的方法,然后通过不同的参数来实现不同效果的

    /**
     * 常用的五个参数如下:
     * 初始化线程池 这里我们不使用Executors.newFixedThreadPool()方式,该种方式不推荐使用,
     * 主要是因为默认允许的队列的长度是Integer.MAX_VALUE,可能会造成OOM
     * 第一个参数:corePoolSize: 线程中核心线程数的最大值(能同时运行的最大的线程数)
     * 第二个参数:maximumPoolSize: 线程池中线程数的最大值
     * 第三个参数:keepAliveTime: 线程存活时间
     * 第四个参数:unit:时间单位
     * 第五个参数:BlockingQueue: 用于缓存任务的队列 这里使用 ArrayBlockingQueue 这个是有界队列
     */
    private ExecutorService threadPool = new ThreadPoolExecutor(this.corePoolSize, this.maximumPoolSize,
            this.keepAliveTime, TimeUnit.SECONDS,
            new ArrayBlockingQueue(this.corePoolSize));

在实际的应用中,我们将线程池使用单例的模式来实现线程池的单例,这里我们使用静态内部类的方式来实现单例模式,做到线程的绝对安全

    /**
     * 使用静态内部类来实现单例的模式(绝对的线程安全)
     */
    private static class Singleton {
        /**
         * 私有的静态变量,确保该变量不会被外部调用
         */
        private static RequestThreadPool requestThreadPool;

        /**
         * 静态代码块在类初始化时执行一次
         */
        static {
            requestThreadPool = new RequestThreadPool();
        }

        /**
         * 静态内部类对外提供实例的获取方法
         *
         * @return
         */
        public static RequestThreadPool getInstance() {
            return requestThreadPool;
        }
    }

使用静态内部类的方式来实现单例模式主要有以下的优点:

  • 外部内加载的时候,不需要立即加载内部类,内部类不被加载,就不会初始化,故而不占用内存
  • getInstance被调用时,才会去初始化实例,第一次调用getInstance会导致虚拟机加载实例,这种方法不仅能确保线程的安全,也能保证单例的唯一性

结合上述的线程池实例化和单例模式,我们整合代码如下:

    /**
     * Copyright © 2018 五月工作室. All rights reserved.
     *
     * @Package com.amos.common.thread
     * @ClassName RequestThreadPool
     * @Description 使用线程池来管理线程,该线程池必须是单例的
     * @Author Amos
     * @Modifier
     * @Date 2019/7/14 16:47
     * @Version 1.0
     **/
    @Component
    public class RequestThreadPool {
        /**
         * 核心线程数
         */
        @Value("${request.queue.corePoolSize:10}")
        private Integer corePoolSize;
        /**
         * 线程池最大线程数
         */
        @Value("${request.queue.maximumPoolSize:20}")
        private Integer maximumPoolSize;
    
        /**
         * 线程最大存活时间
         */
        @Value("${request.queue.keepAliveTime:60}")
        private Long keepAliveTime;
    
        /**
         * 初始化线程池 这里我们不使用Executors.newFixedThreadPool()方式,该种方式不推荐使用,
         * 主要是因为默认允许的队列的长度是Integer.MAX_VALUE,可能会造成OOM
         * 第一个参数:corePoolSize: 线程中核心线程数的最大值(能同时运行的最大的线程数)
         * 第二个参数:maximumPoolSize: 线程池中线程数的最大值
         * 第三个参数:keepAliveTime: 线程存活时间
         * 第四个参数:unit:时间单位
         * 第五个参数:BlockingQueue: 用于缓存任务的队列 这里使用 ArrayBlockingQueue 这个是有界队列
         */
        private ExecutorService threadPool = new ThreadPoolExecutor(this.corePoolSize, this.maximumPoolSize,
                this.keepAliveTime, TimeUnit.SECONDS,
                new ArrayBlockingQueue(this.corePoolSize));
    
    
        /**
         * 构造器私有化,这样就不能通过new来创建实例对象
         * <p>
         * 类实例化的时候 ,初始化队列的大小,并且绑定队列和线程池以及队列与线程的关系
         * <p>
         * 初始化指定数量的队列
         */
        private RequestThreadPool() {
            /**
             *缓存队列集合来管理所有的缓存队列
             */
            RequestQueue requestQueue = RequestQueue.getInstance();
            for (int i = 0; i < this.corePoolSize; i++) {
                /**
                 * 缓存队列使用Request 接口来作为泛型,将可以将队列的类型添加定义,同时也可以通过多态的特性来实现子类的扩展
                 * 目前Request只是定义,业务可以之后实现
                 */
                ArrayBlockingQueue<Request> queue = new ArrayBlockingQueue<>(this.corePoolSize);
                requestQueue.add(queue);
                this.threadPool.submit(new RequestThread(queue));
            }
        }
    
        /**
         * 使用静态内部类来实现单例的模式(绝对的线程安全)
         */
        private static class Singleton {
            /**
             * 私有的静态变量,确保该变量不会被外部调用
             */
            private static RequestThreadPool requestThreadPool;
    
            /**
             * 静态代码块在类初始化时执行一次
             */
            static {
                requestThreadPool = new RequestThreadPool();
            }
    
            /**
             * 静态内部类对外提供实例的获取方法
             *
             * @return
             */
            public static RequestThreadPool getInstance() {
                return requestThreadPool;
            }
        }
    
        /**
         * 请求线程池类对外提供获取实例的方法 由于外部类没有RequestThreadPool的实例对象,所以除了该方法,外部类无法创建额外的RequestThreadPool对象
         *
         * @return
         */
        public static RequestThreadPool getInstance() {
            return Singleton.getInstance();
        }
    
    
    }

其中RequestQueue如下:

    /**
     * Copyright © 2018 嘉源锐信. All rights reserved.
     *
     * @Project: rabbitmq
     * @ClassName: RequestQueue
     * @Package: com.amos.common.request
     * @author: zhuqb
     * @Description: 请求的队列
     * <p/>
     * 这里需要使用单例模式来确保请求的队列的对象只有一个
     * @date: 2019/7/15 0015 下午 14:18
     * @Version: V1.0
     */
    public class RequestQueue {
        /**
         * 构造器私有化,这样就不能通过new来创建实例对象
         * 这里构造器私有化 这点跟枚举一样的,所以我们也可以通过枚举来实现单例模式,详见以后的博文
         */
        private RequestQueue() {
        }
    
        /**
         * 内存队列
         */
        private List<ArrayBlockingQueue<Request>> queues = new ArrayList<ArrayBlockingQueue<Request>>();
    
        /**
         * 私有的静态内部类来实现单例
         */
        private static class Singleton {
            private static RequestQueue queue;
    
            static {
                queue = new RequestQueue();
            }
    
            private static RequestQueue getInstance() {
                return queue;
            }
        }
    
        /**
         * 获取 RequestQueue 对象
         *
         * @return
         */
        public static RequestQueue getInstance() {
            return Singleton.getInstance();
        }
    
        /**
         * 向容器中添加队列
         *
         * @param queue
         */
        public void add(ArrayBlockingQueue<Request> queue) {
            this.queues.add(queue);
        }
    
    }

RequestThread的代码如下:

    /**
     * Copyright © 2018 嘉源锐信. All rights reserved.
     *
     * @Project: rabbitmq
     * @ClassName: RequestThread
     * @Package: com.amos.common.thread
     * @author: zhuqb
     * @Description: 执行请求的工作线程
     * <p/>
     * 线程和队列进行绑定,然后再线程中处理对应的业务逻辑
     * @date: 2019/7/15 0015 下午 14:34
     * @Version: V1.0
     */
    public class RequestThread implements Callable<Boolean> {
        /**
         * 队列
         */
        private ArrayBlockingQueue<Request> queue;
    
        public RequestThread(ArrayBlockingQueue<Request> queue) {
            this.queue = queue;
        }
    
        /**
         * 方法中执行具体的业务逻辑
         * TODO
         *
         * @return
         * @throws Exception
         */
        @Override
        public Boolean call() throws Exception {
            return true;
        }
    }

具体的代码详见:
rabbitMQ

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值