美文网首页
必备技能——使用线程池来初始化缓冲队列

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

作者: AmosZhu | 来源:发表于2019-07-16 22:13 被阅读0次

    在我们的业务场景中,经常会遇到一些需要在系统启动时就需要手动缓存一下线程,方便我们去使用已有的线程去处理一些业务,降低系统资源的消耗等,今天我们就来讲解下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

    相关文章

      网友评论

          本文标题:必备技能——使用线程池来初始化缓冲队列

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