记使用阻塞队列的坑

由于项目需求,使用多线程处理数据,期间数据量后期可能达到百万甚至千万级别,所以考虑使用阻塞队列存储,所有数据


使用的是 LinkedBlockingQueue 队列,大小设置为   10000     每次数据库中只取号码list  每个list大小  500   所以可以支撑数据量是  500W

下面的代码是没有问题的

贴源码:

/**
     * 获取所有号码列表 存入队列中
     * @param gprsTime
     * @return
     */
    public BlockingQueue getAllPhoneNumber(String gprsTime){

        BlockingQueue> blockingQueue = new LinkedBlockingQueue>(BLOCK_QUEUE_COUNT);
        //当前时间戳
        long currentTime= System.currentTimeMillis();
        int offset = 0;
        int limit = 500;

        List phoneNumberList = iotPhoneNumberInfoBiz.getIotPhoneNumberInfoByQueryGprsTime(gprsTime, currentTime, offset, limit);

        while(null != phoneNumberList && phoneNumberList.size() > 0){
            blockingQueue.add(phoneNumberList);
            offset += limit + 1;
            phoneNumberList = iotPhoneNumberInfoBiz.getIotPhoneNumberInfoByQueryGprsTime(gprsTime, currentTime, offset, limit);
        }
        LOGGER.info("phone number queue end " + blockingQueue.size());
        return blockingQueue;
    }

取出所有需要处理的数据之后,使用线程池针对每个号码,单独处理所需要的数据


贴源码:

没有直接使用 线程池 newFixedThreadPool,因为这个线程池中默认使用的是无界队列,可能会把机器内存占满,所以自己封装线程池

    public static ExecutorService newFixedThreadPool(int nThreads) {  
            return new ThreadPoolExecutor(nThreads, nThreads,  
                                          0L, TimeUnit.MILLISECONDS,  
                                          new LinkedBlockingQueue());  
        }  
线程池大小 10,阻塞队列大小10 k

//缓存所有需要查询的号码
            BlockingQueue> allPhoneNumber = getAllPhoneNumber(gprsTime);

            //使用阻塞队列和线程池处理
            BlockingQueue queue = new LinkedBlockingQueue(THREAD_POOL_COUNT);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_POOL_COUNT, THREAD_POOL_COUNT, THREAD_POOL_COUNT, TimeUnit.MINUTES, queue, new ThreadPoolExecutor.CallerRunsPolicy());


            List phoneNumberList = null;
            LOGGER.info("ThreadPool begin :");
            while ((phoneNumberList = allPhoneNumber.poll()) != null) {//错误代码是使用了 allPhoneNumber.take()方法

                LOGGER.info("phoneNumberList :" + phoneNumberList.size());
                final List finalPhoneNumberList = phoneNumberList;
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        proceedDeal(finalPhoneNumberList);
                    }
                });
                totalCount += finalPhoneNumberList.size();
            }

            executor.shutdown();
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
            LOGGER.info("ThreadPool end ");

遇到的坑就是 while循环的位置,编写的本意是循环取 队列中的数据,直到队列为空,退出循环   ,但是之前一直用的是队列的take方法,造成线程跑一半就不跑了,查了半天,最后发现是take方法,定义是 一出并返回队列头部元素,如果队列为空,则堵塞,哎,只怪自己没有搞清楚队列的方法,用错了,导致程序一直阻塞,完全停不下来,日志也看不出来


最后附加下 队列的几个常用方法,根据需求谨慎使用:

add        增加一个元索                     如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常
offer       添加一个元素并返回true       如果队列已满,则返回false
poll         移除并返问队列头部的元素    如果队列为空,则返回null
peek       返回队列头部的元素             如果队列为空,则返回null
put         添加一个元素                      如果队列满,则阻塞
take        移除并返回队列头部的元素     如果队列为空,则阻塞

感觉自己好喽逼,特此一记

决定以后犯过一个错就写一个博客,10年后看看饭了多少二逼错误


你可能感兴趣的:(java学习)