`

如何充分利用多核CPU,计算很大的List中所有整数的和

 
阅读更多

引用
前几天在网上看到一个淘宝的面试题:有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果。

一:分析题目 
从题中可以看到“很大的List”以及“充分利用多核CPU”,这就已经充分告诉我们要采用多线程(任务)进行编写。具体怎么做呢?大概的思路就是分割List,每一小块的List采用一个线程(任务)进行计算其和,最后等待所有的线程(任务)都执行完后就可得到这个“很大的List”中所有整数的和。 
二:具体分析和技术方案 
既然我们已经决定采用多线程(任务),并且还要分割List,每一小块的List采用一个线程(任务)进行计算其和,那么我们必须要等待所有的线程(任务)完成之后才能得到正确的结果,那么怎么才能保证“等待所有的线程(任务)完成之后输出结果呢”?这就要靠java.util.concurrent包中的CyclicBarrier类了。它是一个同步辅助类,它允许一组线程(任务)互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程(任务)的程序中,这些线程(任务)必须不时地互相等待,此时 CyclicBarrier 很有用。简单的概括其适应场景就是:当一组线程(任务)并发的执行一件工作的时候,必须等待所有的线程(任务)都完成时才能进行下一个步骤。具体技术方案步骤如下: 
  • 分割List,根据采用的线程(任务)数平均分配,即list.size()/threadCounts。
  • 定义一个记录“很大List”中所有整数和的变量sum,采用一个线程(任务)处理一个分割后的子List,计算子List中所有整数和(subSum),然后把和(subSum)累加到sum上。
  • 等待所有线程(任务)完成后输出总和(sum)的值。

示意图如下: 

三:详细编码实现 
代码中有很详细的注释,这里就不解释了。 
Java代码  收藏代码
  1. /** 
  2.  * 计算List中所有整数的和<br> 
  3.  * 采用多线程,分割List计算 
  4.  * @author 飞雪无情 
  5.  * @since 2010-7-12 
  6.  */  
  7. public class CountListIntegerSum {  
  8.     private long sum;//存放整数的和  
  9.     private CyclicBarrier barrier;//障栅集合点(同步器)  
  10.     private List<Integer> list;//整数集合List  
  11.     private int threadCounts;//使用的线程数  
  12.     public CountListIntegerSum(List<Integer> list,int threadCounts) {  
  13.         this.list=list;  
  14.         this.threadCounts=threadCounts;  
  15.     }  
  16.     /** 
  17.      * 获取List中所有整数的和 
  18.      * @return 
  19.      */  
  20.     public long getIntegerSum(){  
  21.         ExecutorService exec=Executors.newFixedThreadPool(threadCounts);  
  22.         int len=list.size()/threadCounts;//平均分割List  
  23.         //List中的数量没有线程数多(很少存在)  
  24.         if(len==0){  
  25.             threadCounts=list.size();//采用一个线程处理List中的一个元素  
  26.             len=list.size()/threadCounts;//重新平均分割List  
  27.         }  
  28.         barrier=new CyclicBarrier(threadCounts+1);  
  29.         for(int i=0;i<threadCounts;i++){  
  30.             //创建线程任务  
  31.             if(i==threadCounts-1){//最后一个线程承担剩下的所有元素的计算  
  32.                 exec.execute(new SubIntegerSumTask(list.subList(i*len,list.size())));  
  33.             }else{  
  34.                 exec.execute(new SubIntegerSumTask(list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1))));  
  35.             }  
  36.         }  
  37.         try {  
  38.             barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处  
  39.         } catch (InterruptedException e) {  
  40.             System.out.println(Thread.currentThread().getName()+":Interrupted");  
  41.         } catch (BrokenBarrierException e) {  
  42.             System.out.println(Thread.currentThread().getName()+":BrokenBarrier");  
  43.         }  
  44.         exec.shutdown();  
  45.         return sum;  
  46.     }  
  47.     /** 
  48.      * 分割计算List整数和的线程任务 
  49.      * @author lishuai 
  50.      * 
  51.      */  
  52.     public class SubIntegerSumTask implements Runnable{  
  53.         private List<Integer> subList;  
  54.         public SubIntegerSumTask(List<Integer> subList) {  
  55.             this.subList=subList;  
  56.         }  
  57.         public void run() {  
  58.             long subSum=0L;  
  59.             for (Integer i : subList) {  
  60.                 subSum += i;  
  61.             }    
  62.             synchronized(CountListIntegerSum.this){//在CountListIntegerSum对象上同步  
  63.                 sum+=subSum;  
  64.             }  
  65.             try {  
  66.                 barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处  
  67.             } catch (InterruptedException e) {  
  68.                 System.out.println(Thread.currentThread().getName()+":Interrupted");  
  69.             } catch (BrokenBarrierException e) {  
  70.                 System.out.println(Thread.currentThread().getName()+":BrokenBarrier");  
  71.             }  
  72.             System.out.println("分配给线程:"+Thread.currentThread().getName()+"那一部分List的整数和为:\tSubSum:"+subSum);  
  73.         }  
  74.           
  75.     }  
  76.       
  77. }  

有人可能对barrier=new CyclicBarrier(threadCounts+1);//创建的线程数和主线程main有点不解,不是采用的线程(任务)数是threadCounts个吗?怎么为CyclicBarrier设置的给定数量的线程参与者比我们要采用的线程数多一个呢?答案就是这个多出来的一个用于控制main主线程的,主线程也要等待,它要等待其他所有的线程完成才能输出sum值,这样才能保证sum值的正确性,如果main不等待的话,那么结果将是不可预料的。 
Java代码  收藏代码
  1. /** 
  2.  * 计算List中所有整数的和测试类 
  3.  * @author 飞雪无情 
  4.  * @since 2010-7-12 
  5.  */  
  6. public class CountListIntegerSumMain {  
  7.   
  8.     /** 
  9.      * @param args 
  10.      */  
  11.     public static void main(String[] args) {  
  12.         List<Integer> list = new ArrayList<Integer>();  
  13.         int threadCounts = 10;//采用的线程数  
  14.         //生成的List数据  
  15.         for (int i = 1; i <= 1000000; i++) {  
  16.             list.add(i);  
  17.         }  
  18.         CountListIntegerSum countListIntegerSum=new CountListIntegerSum(list,threadCounts);  
  19.         long sum=countListIntegerSum.getIntegerSum();  
  20.         System.out.println("List中所有整数的和为:"+sum);  
  21.     }  
  22.   
  23. }  

四:总结 
本文主要通过一个淘宝的面试题为引子,介绍了并发的一点小知识,主要是介绍通过CyclicBarrier同步辅助器辅助多个并发任务共同完成一件工作。Java SE5的java.util.concurrent引入了大量的设计来解决并发问题,使用它们有助于我们编写更加简单而健壮的并发程序。 

附mathfox提到的ExecutorService.invokeAll()方法的实现 
这个不用自己控制等待,invokeAll执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。sdh5724也说用了同步,性能不好。这个去掉了同步,根据返回结果的 Future 列表相加就得到总和了。
Java代码  收藏代码
  1. /** 
  2.  * 使用ExecutorService的invokeAll方法计算 
  3.  * @author 飞雪无情 
  4.  * 
  5.  */  
  6. public class CountSumWithCallable {  
  7.   
  8.     /** 
  9.      * @param args 
  10.      * @throws InterruptedException  
  11.      * @throws ExecutionException  
  12.      */  
  13.     public static void main(String[] args) throws InterruptedException, ExecutionException {  
  14.         int threadCounts =19;//使用的线程数  
  15.         long sum=0;  
  16.         ExecutorService exec=Executors.newFixedThreadPool(threadCounts);  
  17.         List<Callable<Long>> callList=new ArrayList<Callable<Long>>();  
  18.         //生成很大的List  
  19.         List<Integer> list = new ArrayList<Integer>();  
  20.         for (int i = 0; i <= 1000000; i++) {  
  21.             list.add(i);  
  22.         }  
  23.         int len=list.size()/threadCounts;//平均分割List  
  24.         //List中的数量没有线程数多(很少存在)  
  25.         if(len==0){  
  26.             threadCounts=list.size();//采用一个线程处理List中的一个元素  
  27.             len=list.size()/threadCounts;//重新平均分割List  
  28.         }  
  29.         for(int i=0;i<threadCounts;i++){  
  30.             final List<Integer> subList;  
  31.             if(i==threadCounts-1){  
  32.                 subList=list.subList(i*len,list.size());  
  33.             }else{  
  34.                 subList=list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1));  
  35.             }  
  36.             //采用匿名内部类实现  
  37.             callList.add(new Callable<Long>(){  
  38.                 public Long call() throws Exception {  
  39.                     long subSum=0L;  
  40.                     for(Integer i:subList){  
  41.                         subSum+=i;  
  42.                     }  
  43.                     System.out.println("分配给线程:"+Thread.currentThread().getName()+"那一部分List的整数和为:\tSubSum:"+subSum);  
  44.                     return subSum;  
  45.                 }  
  46.             });  
  47.         }  
  48.         List<Future<Long>> futureList=exec.invokeAll(callList);  
  49.         for(Future<Long> future:futureList){  
  50.             sum+=future.get();  
  51.         }  
  52.         exec.shutdown();  
  53.         System.out.println(sum);  
  54.     }  
  55.   
  56. }  

一些感言 
这篇文章是昨天夜里11点多写好的,我当时是在网上看到了这个题目,就做了一下分析,写了实现代码,由于水平有限,难免有bug,这里感谢xifo等人的指正。这些帖子从发表到现在不到24小时的时间里创造了近9000的浏览次数,回复近100,这是我没有想到的,javaeye很久没这么疯狂过啦。这不是因为我的算法多好,而是因为这个题目、这篇帖子所体现出的意义。大家在看完这篇帖子后不光指正错误,还对方案进行了改进,关键是思考,人的思维是无穷的,只要我们善于发掘,善于思考,总能想出一些意想不到的方案。 

从算法看,或者从题目场景对比代码实现来看,或许不是一篇很好的帖子,但是我说这篇帖子是很有意义的,方案也是在很多场景适用,有时我们可以假设这不是计算和,而是把数据写到一个个的小文件里,或者是分割进行网络传输等等,都有一定的启发,特别是回帖中的讨论。 

单说一下回帖,我建议进来的人尽量看完所有的回帖,因为这里是很多人集思广益的精华,这里有他们分析问题,解决问题的思路,还有每个人提到的解决方案,想想为什么能用?为什么不能用?为什么好?为什么不好?
 

我一直相信:讨论是解决问题、提高水平的最佳方式! 
分享到:
评论

相关推荐

    并行计算简介和多核CPU编程Demo.pdf

    “并行计算简介和多核CPU编程Demo.pdf” 该资源主要介绍了并行计算的概念、多核CPU编程的 Demo 及其实现。下面是从该资源中提取的知识点: 并行计算简介 并行计算是计算机科学中的一种技术,通过将计算任务分解成...

    visual c++让多核CPU占用率达到100%

    在编程领域,尤其是在高性能计算和并行处理中,利用多核CPU的全部能力是一项关键任务。Visual C++作为Microsoft提供的C++开发环境,提供了一种强大的工具——OpenMP(Open Multi-Processing),来帮助程序员实现多...

    利用多cpu或多核cpu求解deform3d案例

    通过对Deform3D软件中多CPU/多核CPU配置方法的学习,我们不仅能够充分利用现有硬件资源提高计算效率,还能更深入地理解高性能计算领域的基础知识和技术要点。希望本篇文章能为您提供有价值的信息和支持,帮助您更好...

    基于多核CPU的并行计算设计

    ### 基于多核CPU的并行计算设计 #### 摘要及核心思想概述 本文探讨了基于多核CPU环境下多线程...通过合理的多线程并行计算设计方案,可以充分发挥多核CPU的潜力,满足科学研究和工程计算中日益增长的大规模计算需求。

    充分发挥cpu多核性能

    在Windows XP时期,由于操作系统对多核处理的支持相对较弱,因此,用户可能会遇到双核CPU性能未被充分利用的问题。描述中提到的“xp3下发挥双核的性能”,指的是在Windows XP Service Pack 3(xp3)这个版本下,通过...

    多核cpu和gpu系统上椭圆曲线点乘并行计算研究.pdf

    多核cpu和gpu系统上椭圆曲线点乘并行计算研究.pdf

    多核CPU测试工具(PI)

    标题中的“多核CPU测试工具(PI)”以及描述中提到的“CPU测试”和“多线程测试多核CPU的工具”暗示了我们要讨论的是针对计算机处理器性能进行评估的工具,特别是那些能够利用多核心处理器并行计算能力的测试软件。...

    基于多核CPU的并行计算设计.pdf

    在这种背景下,如何充分利用单机多核CPU的硬件优势,提高大型计算任务的性能和效率,成为了一个重要的研究方向。 多线程技术是实现单机多核并行计算的核心手段。通过对不同线程数量的测试,可以观察到当线程数量...

    linux-让多核CPU达到指定的CPU使用率脚本

    "linux-让多核CPU达到指定的CPU使用率脚本"是一个旨在提升机器CPU使用率的实用工具,它能帮助系统管理员更好地控制和利用计算资源。下面我们将详细探讨相关知识点。 1. **CPU使用率**: - CPU使用率是指CPU在单位...

    AMD多核CPU补丁XP专用1.3.2.0053版

    在早期的操作系统如Windows XP中,对于多核处理器的支持并不完善,可能导致某些程序无法充分利用多核优势,甚至出现运行不稳定、性能下降或崩溃的情况。这个补丁的出现,旨在优化系统对AMD多核CPU的识别和管理,以...

    WindowsVC++获取多核CPU各核使用率

    《Windows系统CPU内存网络性能统计第三篇 CPU 多核CPU各核使用率 C#》 http://blog.csdn.net/morewindows/article/details/8678382 配套程序,能获取多核CPU各核的使用率,已经测试,能在WinXP及Win7运行。

    用微机多核CPU并行方式运转WRF模式.pdf

    本文主要探讨了如何利用微机多核CPU的并行方式运行Weather Research and Forecasting (WRF)模式,这是一种用于大气科学中的区域中尺度数值预报模型。文章指出,随着WRF模式分辨率的提高,对计算能力的需求呈量级式...

    多核多线程杂谈-并行计算

    多线程可以充分利用多核处理器的优势,但在编写多线程程序时需要注意线程安全问题,即确保数据的一致性和避免竞态条件。 #### 4. 铁路系统与多核多线程的类比 为了更好地理解多核多线程的概念,可以通过与现实生活...

    面向多核CPU和GPU平台的数据库星形连接优化.pdf

    该算法通过将数据组织成向量形式,利用多核CPU和GPU的并行计算能力,减少数据处理中的数据移动和内存访问,从而降低连接操作的物化代价。向量化技术可以有效提升处理速度,特别是在多核环境下,通过并行处理大量数据...

    MS图像分割在GPU和多核CPU上运行性能研究.pdf

    但是,GPU和多核CPU在MS图像分割中的应用也存在一些挑战和限制,例如数据传输和存储、算法优化、并行计算等。 本研究旨在探讨MS图像分割在GPU和多核CPU上运行性能的研究,旨在探讨两种平台在MS图像分割中的应用和...

    windows系统多核CPU内核线程管理

    然而,传统的单核处理器设计的操作系统和应用程序并未充分利用多核处理器的优势,尤其是在实时性和资源分配方面存在局限性。Windows操作系统,作为全球最广泛使用的桌面操作系统之一,其多核CPU内核线程管理机制对于...

    基于多核CPU的并行程序在指控系统中的应用.pdf

    本资源主要介绍了基于多核CPU的并行程序在指控系统中的应用,包括多核处理器的背景和原理、并行程序设计在指控系统中的应用前景,以及基于多核CPU的并行程序的性能验证和分析。 关键词:多核CPU、并行程序设计、...

    matlab开发-多核的多准备处理

    在MATLAB中进行多核的多任务预处理是提高计算效率和速度的重要手段。这个压缩包文件集合提供了在多个核心或机器上实现并行处理的功能,尤其适用于控制系统的设计与分析,其中涉及到的关键知识点如下: 1. **并行...

Global site tag (gtag.js) - Google Analytics