提取出某日访问网站次数最多的那K个IP之并发版

前边提到了单线程的实现,这里贴出多线程版,此处主要用多线程去处理hash后的小文件:

 

Java代码   收藏代码
  1. package com.kingdee.gmis.mass.data.ips;  
  2.   
  3. import static com.kingdee.gmis.mass.data.ips.MassIP.K10;  
  4. import static com.kingdee.gmis.mass.data.ips.MassIP.getPartitionFile;  
  5. import static com.kingdee.gmis.mass.data.ips.MassIP.partationCount;  
  6. import static com.kingdee.gmis.mass.data.ips.MassIP.printResult;  
  7. import static com.kingdee.gmis.mass.data.ips.MassIP.*;  
  8.   
  9. import java.io.BufferedInputStream;  
  10. import java.io.DataInputStream;  
  11. import java.io.File;  
  12. import java.io.FileInputStream;  
  13. import java.io.IOException;  
  14. import java.util.HashMap;  
  15. import java.util.Map;  
  16. import java.util.concurrent.atomic.AtomicInteger;  
  17.   
  18. import com.kingdee.gmis.common.TopNHeap;  
  19. import com.kingdee.gmis.mass.data.ips.MassIP.IPInfo;  
  20.   
  21. public class ConcurrentMassIP {  
  22.   
  23.     private static int workerCnt = 3;  
  24.   
  25.     /** 
  26.      * @param args 
  27.      * @throws Exception 
  28.      */  
  29.     public static void main(String[] args) throws Exception {  
  30. //       generateMassIp("ip", "ips.txt", 100000000);  
  31. //       generatePartitionFile("ip", "ips.txt", 100);  
  32.         searchTopN(20);  
  33.     }  
  34.   
  35.     private static AtomicInteger curIdx = new AtomicInteger(-1);  
  36.     static File[] smallFiles;  
  37.     static TopNHeap<IPInfo> destHeap;  
  38.   
  39.     /** 
  40.      * 查找出现次数最多的K个ip地址 
  41.      *  
  42.      * @param count 
  43.      * @throws Exception 
  44.      */  
  45.     public static void searchTopN(int count) throws Exception {  
  46.         Thread.sleep(10000);  
  47.         long start = System.currentTimeMillis();  
  48.         smallFiles = getPartitionFile("ip", partationCount);  
  49.         destHeap = new TopNHeap<MassIP.IPInfo>(count);  
  50.         int cnt = workerCnt;  
  51.         synchronized (lock) {  
  52.             for (int i = 0; i < cnt; i++) {  
  53.                 new Thread(new Worker(i, count)).start();  
  54.             }  
  55.             lock.wait();  
  56.             printResult(destHeap);  
  57.         }  
  58.         System.out.println("Total spend " + (System.currentTimeMillis() - start) + " ms");  
  59.     }  
  60.   
  61.     static Object lock = new Object();  
  62.   
  63.     public static synchronized void mergeToResult(TopNHeap<IPInfo> srcHeap) {  
  64.         try {  
  65.             destHeap.mergeHeap(srcHeap);  
  66.         } finally {  
  67.             if (--workerCnt == 0) {  
  68.                 synchronized (lock) {  
  69.                     lock.notify();  
  70.                 }  
  71.             }  
  72.         }  
  73.     }  
  74.   
  75.     static class Worker implements Runnable {  
  76.         private int topCount;  
  77.         private int id;  
  78.   
  79.         public Worker(int id, int count) {  
  80.             this.id = id;  
  81.             this.topCount = count;  
  82.         }  
  83.   
  84.         @Override  
  85.         public void run() {  
  86.             int curFileIdx;  
  87.             DataInputStream dis = null;  
  88.             Map<Integer, Integer> ipCountMap = new HashMap<Integer, Integer>();  
  89.             TopNHeap<IPInfo> heap = new TopNHeap<IPInfo>(topCount);  
  90.             int processCnt = 0;  
  91.             while ((curFileIdx = curIdx.addAndGet(1)) < partationCount) {  
  92.                 processCnt++;  
  93.                 ipCountMap.clear();  
  94.                 try {  
  95.                     dis = new DataInputStream(new BufferedInputStream(  
  96.                             new FileInputStream(smallFiles[curFileIdx]), K10));  
  97.                     while (dis.available() > 0) {  
  98.                         int ip = dis.readInt();  
  99.                         Integer cnt = ipCountMap.get(ip);  
  100.                         ipCountMap.put(ip, cnt == null ? 1 : cnt + 1);  
  101.                     }  
  102.                     searchMostCountIps(ipCountMap, heap);  
  103.                 } catch (Exception e) {  
  104.                     throw new RuntimeException(e);  
  105.                 } finally {  
  106.                     if (dis != null) {  
  107.                         try {  
  108.                             dis.close();  
  109.                         } catch (IOException e) {  
  110.                             e.printStackTrace();  
  111.                         }  
  112.                     }  
  113.                 }  
  114.             }  
  115.             System.out.println("Thread " + this.id + " process " + processCnt  
  116.                     + " files.");  
  117.             mergeToResult(heap);  
  118.         }  
  119.     }  
  120. }  

 

      测试了下,3线程是120s左右,串行是320s左右,的确提高了很多。测试机子是4核cpu,如果启4个线程,机器都变得很卡,cpu居高不下。另jvm启动参数为:

 

Java代码   收藏代码
  1. -server  
  2. -Xmx1024m  
  3. -Xms1024m  
  4. -Xmn600m  
  5. -XX:+UseConcMarkSweepGC  
  6. -XX:ParallelGCThreads=4  

      因为此处理过程中,大多数对象存活周期并不长,所以可以把新生代设置大一些。堆初始化设置大些,避免minor gc的时候才去扩展内存大小,因为可以预料到程序一旦启动,加载的内存的东西就会很多。

另外,此处垃圾收集器是用了cms,老生代内存回收就用了cms,新生代用了PurNew,并行收集的,设置gc线程数等于cpu内核数。当然这里也可以设置成-XX:+UseParallelGC、-XX:+UseParallelOldGC都可以,此处主要是新生代内存回收频繁,所以一定要把新生代设置成并发或并行版本的。

通过-server把虚拟机启动为server模式,这样运行时候会启用c2级别的JIT优化,能获得更高质量的编译代码。当然server模式下启动的jvm,默认使用的gc收集器跟-XX:UseParallelGC使用的一样

 

 

http://yueyemaitian.iteye.com/blog/1207913

你可能感兴趣的:(并发)