Java环形缓冲区+生产消费模型及同步开销测试

来源:互联网 发布:autodesk主要软件 编辑:程序博客网 时间:2024/05/16 07:35

代码是为了使用环形缓冲区实现消费者生产者功能:

package ringBuffer.parallel;import java.io.IOException;import java.io.RandomAccessFile;import java.util.LinkedHashMap;import java.util.Map;public class Producer { public static void main(String[] args) {long start = System.currentTimeMillis() ;Producer mc = new Producer() ;Consumer cr = mc.new Consumer() ;cr.start() ;for (Integer j = 0; j < 5; j++) {LinkedHashMap<Integer, String> msgs = new LinkedHashMap<Integer, String>() ;for (Integer i = 0; i < 100000; i++) {msgs.put(i,j.toString()) ;}cr.addToMsgBuf(msgs);}try {//cr.waitUntilEmpty();cr.interrupt() ;cr.join() ;   //这里主线程会否比子线程先跑完? 加上join} catch (InterruptedException e) {e.printStackTrace();}System.out.println(" last : "+ ( System.currentTimeMillis() -start));System.exit(0);}class Consumer extends Thread {int bufferSize = 2 * 1024 * 1024;private byte[] buffer = new byte[bufferSize];private Integer pos = 0;private Integer prePos = 0;String outputFile = "F:\\test\\ringBuf.txt";    RandomAccessFile raf;    StringBuffer sb = null;    LinkedHashMap<Integer, String> cMsgs = new LinkedHashMap<Integer, String>() ;    ;public Consumer(){    try {    raf = new RandomAccessFile(outputFile, "rw");} catch (IOException e) {e.printStackTrace();}}public void run() {try {   while (!Thread.interrupted()) {   synchronized (cMsgs){   while (cMsgs.isEmpty()) {   cMsgs.wait();               }   sb = new StringBuffer();   for (Map.Entry<Integer, String> v : cMsgs.entrySet()) {   sb.append(v.getKey()).append("-").append(v.getValue()).append("V") ;   }   cMsgs.clear() ;   cMsgs.notify() ;   }   sb.append("S");     byte[] buf = sb.toString().getBytes();   int length = buf.length ;   pos = prePos + length ;   int bufPos = 0;   if (pos > bufferSize && prePos < bufferSize) {       bufPos = bufferSize - prePos;System.arraycopy(buf, 0, buffer, prePos, bufPos);  prePos = pos = 0;long fileLength = raf.length();  raf.seek(fileLength);  raf.write(buffer);     }else     System.arraycopy(buf, 0, buffer, prePos, length);     if(bufPos==0){      prePos = pos;   }else{       System.arraycopy(buf, bufPos, buffer, prePos, length-bufPos);    prePos = prePos +  length-bufPos ;   pos =prePos ;   }   }   long fileLength = raf.length();     raf.seek(fileLength);     raf.write(buffer, 0, prePos);     raf.close();    }catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}  }public void addToMsgBuf(LinkedHashMap<Integer, String> currentStepMsgs) { synchronized (cMsgs) { try {     while(!cMsgs.isEmpty()){     cMsgs.wait() ;     }     cMsgs.putAll(currentStepMsgs); cMsgs.notify() ;} catch (InterruptedException e) {e.printStackTrace();}     }}public void waitUntilEmpty() throws InterruptedException{synchronized (cMsgs) { while(!cMsgs.isEmpty()){  cMsgs.wait(); }        }}}}

package ringBuffer.parallel;import java.io.IOException;import java.io.RandomAccessFile;public class Producer2 { public static void main(String[] args) {long start = System.currentTimeMillis() ;Producer2 pr = new Producer2() ;Consumer cr = pr.new Consumer() ;cr.start() ;for (Integer j = 0; j < 5; j++) {StringBuffer sb = new StringBuffer();for (Integer i = 0; i < 100000; i++) {sb.append(i.toString()).append("-").append(j.toString() + "V");}sb.append("S");byte[] msgs = sb.toString().getBytes();//System.out.println(msgs.length);cr.addToMsgBuf(msgs);}try {cr.interrupt() ; cr.join() ;  //这里主线程会否比子线程先跑完? 加上join} catch (InterruptedException e) {e.printStackTrace();}System.out.println(" last : "+ ( System.currentTimeMillis() -start));}class Consumer extends Thread {int bufferSize = 2 * 1024 * 1024;private byte[] buffer = new byte[bufferSize];private Integer pos = 0;private Integer prePos = 0;String outputFile = "F:\\test\\ringBuf.txt";        private int totalLength = 0;    RandomAccessFile raf;public Consumer(){    try {    raf = new RandomAccessFile(outputFile, "rw");} catch (IOException e) {e.printStackTrace();}}public void run() {try {while (!Thread.interrupted()) {synchronized (buffer) {while (pos > bufferSize ) { totalLength = totalLength + prePos ;    long fileLength = raf.length();         raf.seek(fileLength);         raf.write(buffer) ;  prePos = prePos % bufferSize ;pos = prePos;  buffer.notify() ;   }}}totalLength = totalLength + prePos ;long fileLength = raf.length();        raf.seek(fileLength);        raf.write(buffer, 0, prePos) ;  raf.close() ;} catch (IOException e) {e.printStackTrace();}}public void addToMsgBuf(byte[] buf) {if (buf == null || buf.length == 0)return;try {synchronized (buffer) {pos = prePos + buf.length ;int bufPos = 0 ;while (pos > bufferSize && prePos<bufferSize) {  bufPos = bufferSize-prePos ;System.arraycopy(buf, 0, buffer, prePos, bufPos);  prePos = bufferSize;System.out.println("Wait Until there is enough space! ");buffer.wait();}System.arraycopy(buf,bufPos, buffer, prePos, buf.length - bufPos);prePos = pos ;if(pos==0&&pos==prePos){  //当buffer写满时prePos = prePos + (buf.length - bufPos) ;pos = prePos ;}}} catch (InterruptedException e) {e.printStackTrace();}}}}

 

以上两种实现方式,从代码功能上看,两者差不多,

空间开销分析:

从理论上看,Producer比Producer2多使用了 new LinkedHashMap<Integer, String>() ;    可能导致比其多占用内存,但实际测试显示, Producer2比Producer内存的占用略高,使用jdk自带的Jconsole(两者均为0次gc收集时),Producer2的堆上已使用内存较高(同一个程序不同次执行时堆内存的使用情况不同,因此需要均为0次gc时比较),


从YongGc Times(没有发生oldGc)看,也是Producer2明显多( jstat -gc pid interval(ms) 次数 > 1.txt   Producer2为289次,远高于Producer的114次,这是我在内存较小的机器上的测试结果,换成大内存机器后,两者的YGC次数相同,均为6次),


时间开销分析:

时间上猜测原因是由于Producer并行粒度太小,  

  producer2直接写环形缓冲区(写缓冲区时同步一次),  而producer 数据先写入map(每次循环同步一次),再到缓冲区

 这里用了int指针代替空的判断,提高了性能.


我机器配置为

为了使结果明显区别,我将main中的j 改为10:

Producer: 

 last : 544


Producer2:

Wait Until there is enough space! 
 last : 426


这里加上直接用顺序实现的,发现同步的开销确实很大,因此环形缓冲区适用于内存不足的情形,且同步开销真是大啊!

package Producer;import java.io.IOException;import java.io.RandomAccessFile;public class ProduceSeq {public static void main(String[] args) {long start = System.currentTimeMillis() ;StringBuffer sb = new StringBuffer();for (Integer j = 0; j < 10; j++) {for (Integer i = 0; i < 100000; i++) {sb.append(i.toString()).append("-").append(j.toString() + "V");}sb.append("S");}     try {    RandomAccessFile raf = new RandomAccessFile("F:\\test\\ringBuf.txt", "rw");    raf.write(sb.toString().getBytes());    raf.close() ; } catch (IOException e) {e.printStackTrace(); }System.out.println(" last : "+ ( System.currentTimeMillis() -start));}}

 last : 317


此外,对cuda的同步实现和java做了比较,发现cuda只有共享存储等简单的同步方式(函数),没有java这种更高级的锁的实现方式处理数据一致性。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 房屋装修后出现质量问题怎么办 华为p9手机音量小怎么办 华为畅享8玩游戏卡怎么办 华为畅享7玩游戏卡怎么办 华为p9升级以后屏幕失灵怎么办 荣耀9青春版玩游戏卡怎么办 华为p9屏幕不亮了怎么办 华为p9入水黑屏怎么办 农信密码忘记了怎么办 小米4g信号差怎么办 手机的调频调制器坏了怎么办 调制解调器的灯一直闪怎么办 691宽带用户名和密码无效怎么办 房间里有狐臭味怎么办 吃冰冻水果胃特别痛怎么办 小米5s屏幕闪烁怎么办 系统重装重启了怎么办 华为p10开不了机怎么办 眼镜腿夹的头疼怎么办 摩托车车头锁坏了怎么办 夏天骑摩托车戴头盔热怎么办 头盔玻璃罩边固定老是掉怎么办 电动车不戴头盔被扣车怎么办 郴州骑电动车没戴头盔怎么办 配置数据源时发现两个版本怎么办 微信占用内存3g怎么办 打印机显示内存已满怎么办 网页显示代理服务器连接失败怎么办 墙内线路断了怎么办 墙里的电线坏了怎么办 鱼竿最前端断了怎么办 下雨天墙壁与管道间漏水怎么办 电饭锅的电线被雨淋了怎么办 钢琴跨八度手短怎么办 弹钢琴手指不灵活怎么办呢? 理发剪不锋利了怎么办 室外宽带线断了怎么办 接宽带光纤太短怎么办 装修光纤网线太短怎么办 宽带入户线断了怎么办 电信有无线没网怎么办