使用阻塞队列批量导入与使用forkjoinPool框架的导入对比
来源:互联网 发布:淘宝实名认证的截图 编辑:程序博客网 时间:2024/06/16 12:23
一:本人使用的环境
jdk1.7+window 10 + oracle 11g
二:使用的技术
1:阻塞队列(BlockingQueue):先进先出(FIFO),生产者-消费者的模式
分为有界队列(ArrayBlockingQueue)、无界队列(linkedBlocingQueue)等,其他的不在此举例
其put()方法:如果队列已满则阻塞,直到队列中减少,才能被唤醒其线程,有机会去争取资源,继续添加元素到队列中。
其take()方法:如果队列中没有元素,则阻塞该线程,直到阻塞队列中put元素到队列中,才能被唤醒线程。
2:使用ForkJoinPool框架:一种将大任务分割成若干的小任务来执行,至于分成多少个,设置其阀值,比如阀值为100,如果小于100,则不分割,超过100,就再次的分 割,其一个重要的特点(也是其框架的一种有效的智能方法来平衡可用线程的工作负载):工作密取(working-stealing),当某个线程的所有任务都现行执行完毕时,就会向其他执行慢的任务队列中偷取任务来完成,从队列的尾部来偷取,(该队列使用的是双端队列),正常情况下都是从队列头部来取任务来执行,只有工作窃取的时候才会从队列的尾部来取任务,需要继承一个计算合并结果的RecursiveTask<T>或一个没有任务返回的RecursiveAction。其中invoke(ForkJoinTask)同步/有返回结果、execute()异步/没有返回任何。submit(ForkJoinTask)异步/返回一个ForkJoinTask
3:使用线程池(ThreadPoolExecutor):可以很大的程度上减少传统的新建(new Thread)以及销毁而带来的性能降低和内存资源消耗的开销。(接口ExecutorService可以执行线程)
1.newFixedThreadPool(int)可以指定固定的线程数,操作无界队列
2.newScheduleThreadPool(int corePoolsize):可以调度一个在给定的延迟后运行。
3.newSingleThreadPool():创建一个单一的线程来执行。
4.newCacheThreadPool():根据需要来创建新的线程,但在可用时将重用先前的构建的线程。
三:使用阻塞队列时:
ExecutorService executorService = Executors.newFixedThreadPool(10);
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(10000);
BlockQueuThread aa = new BlockQueuThread(queue, randomCodeSeg);
executorService.execute(aa);
SegCodeQueueThread codeQueue = null;
for(int i =0 ; i<4;i++){
codeQueue = new SegCodeQueueThread(baseCdsService,codeSegmentDao, codePoolBean, queue);
executorService.execute(codeQueue);
}
BlockQueuThread 读入到队列中的run方法体:
int siez = randomCodeSeg.size();
String aa = "end";
try {
for(int i = 0 ; i< siez ; i++ ){
queue.put(randomCodeSeg.get(i));
System.out.println("线程name:"+Thread.currentThread().getName()+"-->该线程状态:"+Thread.currentThread().getState()+"queue队列长度:"+queue.size());
}
queue.put(aa);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
e.printStackTrace();
SegCodeQueueThread中的run方法体:
boolean bo = false;
// final LinkedList<Object[]> list = new LinkedList<>();
while(!bo){
final String take = queue.take();
final Object[] ob = new Object[2];
ob[0] = codePoolBean.getSEGMENT_ID();
ob[1] = take;
// list.add(ob);
if("end".equals(take)){
System.out.println("结束blockingQueue");
bo = true;
continue;
}
System.out.println("线程name:"+Thread.currentThread().getName()+"-->该线程状态:"+Thread.currentThread().getState()+"queue取出:"+take);
// codeSegmentDao.insertCodeSegNumT(take,codePoolBean);
baseCdsService.update(sql, ob);
}
执行92万条数据,纯插入单张表,但是执行到一定的时候,线程发生死锁;锁死的错误如下:
java.io
.FileOutputStream.writeBytes(Native Method)
java.io
.FileOutputStream.write(FileOutputStream.java:318)
java.io
.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
java.io
.BufferedOutputStream.flush(BufferedOutputStream.java:140)
- 已锁定java.io
.BufferedOutputStream@fe5ea3e
java.io
.PrintStream.write(PrintStream.java:482)
- 已锁定java.io
.PrintStream@40ee52e3
sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
- 已锁定java.io
.OutputStreamWriter@3f759499
java.io
.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
java.io
.PrintStream.write(PrintStream.java:527)
- 已锁定java.io
.PrintStream@40ee52e3
java.io
.PrintStream.print(PrintStream.java:669)
java.io
.PrintStream.println(PrintStream.java:806)
- 已锁定java.io
.PrintStream@40ee52e3
org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:264)
com.uniform.codeSegment.util.SegCodeQueueThread.run(SegCodeQueueThread.java:52)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
java.lang.Thread.run(Thread.java:722)
问题定位:从上面已锁定可以看出,在操作IO的时候出现死锁,在网上搜索下, 原因是system.out.print()的问题,在多线程使用的情况下, 出现这种情况,
有使用log4j,也会log。info()出现问题。
问题解决:将所有执行的操作中去掉system打印语句。
可参考文章:http://blog.csdn.net/qq_24504453/article/details/75028213
使用forkJoinPool框架的时候,同样的批量导入操作,经本人多次的测试,没有发生异常,每秒达到680次。
继承两种方式:一种有返回值的,合并所有的子任务后返回数据(RecursiveTask),还有一种没有返回数据的
(RecursiveAction)
代码如下:
ForkJoinPool forkJoinPool = new ForkJoinPool();
SegCodeTask codeTask = new SegCodeTask(baseCdsService,codeSegmentDao, codePoolBean, randomCodeSeg, 0, randomCodeSeg.size());
// forkJoinPool.execute(codeTask);
Integer invoke = forkJoinPool.invoke(codeTask);
System.out.println("获取已启动但是还未执行完成的线程数目:"+forkJoinPool.getPoolSize());
System.out.println("获取由工作线程队列中的任务总数的计算:"+forkJoinPool.getQueuedTaskCount());
System.out.println("获取从一个线程被另一个线程窃取的总任务数:"+forkJoinPool.getStealCount());
System.out.println("获取活动的线程数目:"+forkJoinPool.getActiveThreadCount());
System.out.println("总任务执行数目:"+invoke);
@Override
protected Integer compute() {
if(end - begin <= mid){
LinkedList<Object[]> list = new LinkedList<>();
try{
long startTime=System.currentTimeMillis();
for(int i= begin ; i<end;i++){
Object[] ob = new Object[2];
ob[0] = codePoolBean.getSEGMENT_ID();
ob[1] = randomCodeSeg.get(i);
list.add(ob);
// String take = randomCodeSeg.get(i);
// codeSegmentDao.insertCodeSegNumT(take,codePoolBean);
}
baseCdsService.batchInsert(sql, list);
// codeSegmentDao.insertCodeSeg(list,codePoolBean);
long endTime=System.currentTimeMillis();
float excTime=(float)(endTime-startTime)/1000;
System.out.println("执行sql语句的时间:"+excTime+"s");
}catch (Exception e) {
// TODO: handle exception
e.getStackTrace();
return 0;
}
return list.size();
}else{
int middle = (end + begin) / 2;
SegCodeTask oneTask = new SegCodeTask(baseCdsService,codeSegmentDao, codePoolBean, randomCodeSeg,begin,middle);
SegCodeTask twoTask = new SegCodeTask(baseCdsService,codeSegmentDao, codePoolBean, randomCodeSeg, middle, end);
invokeAll(oneTask,twoTask);
return oneTask.join() + twoTask.join();
}
特别注意,上述的操作都是使用SPRING 中的jdbc,使用批量插入batchInsert以及单条插入
但是使用mybatis中sqlSession交互数据库,却发现92万根本无法导入成功,都会在几万的时候会锁死,也使用过动态sql的foreach作导入,也不尽人意
不知道是否是mybatis就不支持大批量的导入操作呢?还是sqlsession连接数问题?
- 使用阻塞队列批量导入与使用forkjoinPool框架的导入对比
- XMPP框架的导入与使用
- ForkJoinPool的使用
- 使用事务批量导入数据
- 使用sqlload批量导入数据
- 转载记录一个Java使用jxl实现对Excel批量导入与导出的实例
- 使用NHibernate完成对数据的批量导入
- PuTTY使用笔记:登录设置的批量备份导出/导入
- PuTTY使用笔记:登录设置的批量备份导出/导入
- 使用Java 的jxl 批量导入数据到数据库
- 批量导入大数据表的时候 使用线程池处理
- 使用PDF补丁丁批量导入书签的办法
- 【C#--数据】1.使用SqlBulkCopy批量导入数据库的示例
- 使用多线程优化批量导入的回显功能
- 使用sql批量导入文本数据
- Neo4j 批量数据导入源码使用
- 使用spool 批量导出和导入
- Cassandra使用pycassa批量导入数据
- jvm优化(二)JVM 内存大小设置
- 如何在MATLAB中输入希腊字母
- 给Python新手的一些编码建议
- OS学习笔记——进程与线程
- Android——Greendao封装进行
- 使用阻塞队列批量导入与使用forkjoinPool框架的导入对比
- 想了解机器学习?这 3 种算法你必须要知道
- linux电脑访问android手机存储
- 申请澳大利亚IPART和VEET的LCP测试报告
- 利用memcached,xcache等做PHP缓存优化
- 利用虚拟号码进行一号多条短信接收
- 杭code1004
- Python自然语言处理 10 分析语句的含义
- C语言无符号整数高精度算法试写