由于项目需求,使用多线程处理数据,期间数据量后期可能达到百万甚至千万级别,所以考虑使用阻塞队列存储,所有数据
使用的是 LinkedBlockingQueue 队列,大小设置为 10000 每次数据库中只取号码list 每个list大小 500 所以可以支撑数据量是 500W
下面的代码是没有问题的
贴源码:
/**
* 获取所有号码列表 存入队列中
* @param gprsTime
* @return
*/
public BlockingQueue getAllPhoneNumber(String gprsTime){
BlockingQueue<List<String>> blockingQueue = new LinkedBlockingQueue<List<String>>(BLOCK_QUEUE_COUNT);
//当前时间戳
long currentTime= System.currentTimeMillis();
int offset = 0;
int limit = 500;
List<String> 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<Runnable>());
}
线程池大小 10,阻塞队列大小10 k
//缓存所有需要查询的号码
BlockingQueue<List<String>> allPhoneNumber = getAllPhoneNumber(gprsTime);
//使用阻塞队列和线程池处理
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(THREAD_POOL_COUNT);
ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_POOL_COUNT, THREAD_POOL_COUNT, THREAD_POOL_COUNT, TimeUnit.MINUTES, queue, new ThreadPoolExecutor.CallerRunsPolicy());
List<String> phoneNumberList = null;
LOGGER.info("ThreadPool begin :");
while ((phoneNumberList = allPhoneNumber.poll()) != null) {//错误代码是使用了 allPhoneNumber.take()方法
LOGGER.info("phoneNumberList :" + phoneNumberList.size());
final List<String> 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年后看看饭了多少二逼错误