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

2019-07-16  本文已影响0人  AmosZhu

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

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

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

    /**
     * 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

上一篇下一篇

猜你喜欢

热点阅读