- 浏览: 976756 次
- 性别:
- 来自: 深圳
博客专栏
-
飞雪的Android学习总...
浏览量:146006
文章分类
最新评论
-
lovebingheji:
感谢,看完了
Spring方法注入 -
ruijin5566:
package concurrent;
import ja ...
淘宝面试题:如何充分利用多核CPU,计算很大的List中所有整数的和 -
helonghui:
Nginx在高并发的时候,内存开销比Apache更加有优势!
使用Nginx搭建PHP服务器 -
xjgpeople:
不错,写的非常不错
基于Android的浮动组件,可以用于应用中的新功能展示等等。 -
Bj_junxia:
不允许加入了,呜呜呜。。。。
Android系列教程之五:Activity的生命周期
永久链接:http://flysnow.iteye.com/blog/711162
一:分析题目
从题中可以看到“很大的List”以及“充分利用多核CPU”,这就已经充分告诉我们要采用多线程(任务)进行编写。具体怎么做呢?大概的思路就是分割List,每一小块的List采用一个线程(任务)进行计算其和,最后等待所有的线程(任务)都执行完后就可得到这个“很大的List”中所有整数的和。
二:具体分析和技术方案
既然我们已经决定采用多线程(任务),并且还要分割List,每一小块的List采用一个线程(任务)进行计算其和,那么我们必须要等待所有的线程(任务)完成之后才能得到正确的结果,那么怎么才能保证“等待所有的线程(任务)完成之后输出结果呢”?这就要靠java.util.concurrent包中的CyclicBarrier类了。它是一个同步辅助类,它允许一组线程(任务)互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程(任务)的程序中,这些线程(任务)必须不时地互相等待,此时 CyclicBarrier 很有用。简单的概括其适应场景就是:当一组线程(任务)并发的执行一件工作的时候,必须等待所有的线程(任务)都完成时才能进行下一个步骤。具体技术方案步骤如下:
示意图如下:
三:详细编码实现
代码中有很详细的注释,这里就不解释了。
有人可能对barrier=new CyclicBarrier(threadCounts+1);//创建的线程数和主线程main有点不解,不是采用的线程(任务)数是threadCounts个吗?怎么为CyclicBarrier设置的给定数量的线程参与者比我们要采用的线程数多一个呢?答案就是这个多出来的一个用于控制main主线程的,主线程也要等待,它要等待其他所有的线程完成才能输出sum值,这样才能保证sum值的正确性,如果main不等待的话,那么结果将是不可预料的。
四:总结
本文主要通过一个淘宝的面试题为引子,介绍了并发的一点小知识,主要是介绍通过CyclicBarrier同步辅助器辅助多个并发任务共同完成一件工作。Java SE5的java.util.concurrent引入了大量的设计来解决并发问题,使用它们有助于我们编写更加简单而健壮的并发程序。
附mathfox提到的ExecutorService.invokeAll()方法的实现
这个不用自己控制等待,invokeAll执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。sdh5724也说用了同步,性能不好。这个去掉了同步,根据返回结果的 Future 列表相加就得到总和了。
一些感言
这篇文章是昨天夜里11点多写好的,我当时是在网上看到了这个题目,就做了一下分析,写了实现代码,由于水平有限,难免有bug,这里感谢xifo等人的指正。这些帖子从发表到现在不到24小时的时间里创造了近9000的浏览次数,回复近100,这是我没有想到的,javaeye很久没这么疯狂过啦。这不是因为我的算法多好,而是因为这个题目、这篇帖子所体现出的意义。大家在看完这篇帖子后不光指正错误,还对方案进行了改进,关键是思考,人的思维是无穷的,只要我们善于发掘,善于思考,总能想出一些意想不到的方案。
从算法看,或者从题目场景对比代码实现来看,或许不是一篇很好的帖子,但是我说这篇帖子是很有意义的,方案也是在很多场景适用,有时我们可以假设这不是计算和,而是把数据写到一个个的小文件里,或者是分割进行网络传输等等,都有一定的启发,特别是回帖中的讨论。
单说一下回帖,我建议进来的人尽量看完所有的回帖,因为这里是很多人集思广益的精华,这里有他们分析问题,解决问题的思路,还有每个人提到的解决方案,想想为什么能用?为什么不能用?为什么好?为什么不好?
我一直相信:讨论是解决问题、提高水平的最佳方式!
传说中的map-redurce
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
不会,只是会活锁。你可以在main方法里面试试。
经过验证,不是活锁,证明了我的担心,是个死锁
public static void main(String args[])
{
try {
Thread.currentThread().join();
System.out.println("ok");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ok永远打印不出来,主线程等待所有子线程结束的方法是不是酱紫地,前面一个同学说了一个方法
atominit方法才是正解
Thread.currentThread().join(); 莫非是传说中的永久sleep,必须等待别人来打断?
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
不会,只是会活锁。你可以在main方法里面试试。
经过验证,不是活锁,证明了我的担心,是个死锁
public static void main(String args[])
{
try {
Thread.currentThread().join();
System.out.println("ok");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ok永远打印不出来,主线程等待所有子线程结束的方法是不是酱紫地,前面一个同学说了一个方法
atominit方法才是正解
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
不会,只是会活锁。你可以在main方法里面试试。
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
对这个进行100次测试,发现有时候算出来的答案不正确,最后一个线程的结果没有累加进去,,,
5分配给线程:pool-1-thread-3那一部分List的整数和为: SubSum:100000100000
5分配给线程:pool-1-thread-1那一部分List的整数和为: SubSum:20000100000
5分配给线程:pool-1-thread-2那一部分List的整数和为: SubSum:60000100000
5分配给线程:pool-1-thread-4那一部分List的整数和为: SubSum:140000100000
===List中所有整数的和为:320000400000threadCounts:5
5分配给线程:pool-1-thread-5那一部分List的整数和为: SubSum:180000100000
代码:
sum.addAndGet(subSum);
System.out.println("分配给线程:" + Thread.currentThread().getName()
+ "那一部分List的整数和为:\tSubSum:" + subSum);
lock.release(); // 释放一个锁的计数
这个存在问题的?
代码理论由上往下执行,这个什么时候可能存在这样问题?(以前看单例双重检查加锁,new语句开辟内存区,但还未初始化数据,类似的?)
没这么复杂
应该是主线程执行太快,任务线程还没来得及锁导致的。
这个东西本来是想模拟CountDownLatch的
仔细想了下,这个设计还会有很大的问题,如果其中某个或者某些任务线程起的太快,并且完成的也快,会导致锁计数很快增加并很快减为0,并且在这个时候,还有一些任务线程还没有启动……
解决方法也不是没有,可以将加锁放在主线程,任务线程只release^
还是用CountDownLatch 比较稳妥
ExecuteService invokeall callable 也是推荐的方法
这个做法和楼主的差不多,在Java Collection,List#sublist方法,会创建一个新的java.util.SubList和它的子类,也是利用的偏移量来做的。
这里完全没有必要使用同步,使用AtomicLong就行了,如果大List是一个ArrayList的话,一个足够了,如果是LinkedList的实现,多个AtomicLong就行了。这里顺序不重要,利用Atomic*的内存CAS操作即可。
对这个进行100次测试,发现有时候算出来的答案不正确,最后一个线程的结果没有累加进去,,,
5分配给线程:pool-1-thread-3那一部分List的整数和为: SubSum:100000100000
5分配给线程:pool-1-thread-1那一部分List的整数和为: SubSum:20000100000
5分配给线程:pool-1-thread-2那一部分List的整数和为: SubSum:60000100000
5分配给线程:pool-1-thread-4那一部分List的整数和为: SubSum:140000100000
===List中所有整数的和为:320000400000threadCounts:5
5分配给线程:pool-1-thread-5那一部分List的整数和为: SubSum:180000100000
代码:
sum.addAndGet(subSum);
System.out.println("分配给线程:" + Thread.currentThread().getName()
+ "那一部分List的整数和为:\tSubSum:" + subSum);
lock.release(); // 释放一个锁的计数
这个存在问题的?
代码理论由上往下执行,这个什么时候可能存在这样问题?(以前看单例双重检查加锁,new语句开辟内存区,但还未初始化数据,类似的?)
你说的意思应该是阻塞队列的那种情况,不过这样也有等待,当其中一个线程去取值的时候,其他的线程就要等着。
分割是为了分配任务,就像大家都敢一个活,你做这点,我做这点,咱俩互不影响,都干完了活也就完了。
引用
前几天在网上看到一个淘宝的面试题:有一个很大的整数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)的值。
示意图如下:
三:详细编码实现
代码中有很详细的注释,这里就不解释了。
/** * 计算List中所有整数的和<br> * 采用多线程,分割List计算 * @author 飞雪无情 * @since 2010-7-12 */ public class CountListIntegerSum { private long sum;//存放整数的和 private CyclicBarrier barrier;//障栅集合点(同步器) private List<Integer> list;//整数集合List private int threadCounts;//使用的线程数 public CountListIntegerSum(List<Integer> list,int threadCounts) { this.list=list; this.threadCounts=threadCounts; } /** * 获取List中所有整数的和 * @return */ public long getIntegerSum(){ ExecutorService exec=Executors.newFixedThreadPool(threadCounts); int len=list.size()/threadCounts;//平均分割List //List中的数量没有线程数多(很少存在) if(len==0){ threadCounts=list.size();//采用一个线程处理List中的一个元素 len=list.size()/threadCounts;//重新平均分割List } barrier=new CyclicBarrier(threadCounts+1); for(int i=0;i<threadCounts;i++){ //创建线程任务 if(i==threadCounts-1){//最后一个线程承担剩下的所有元素的计算 exec.execute(new SubIntegerSumTask(list.subList(i*len,list.size()))); }else{ exec.execute(new SubIntegerSumTask(list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1)))); } } try { barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处 } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+":Interrupted"); } catch (BrokenBarrierException e) { System.out.println(Thread.currentThread().getName()+":BrokenBarrier"); } exec.shutdown(); return sum; } /** * 分割计算List整数和的线程任务 * @author lishuai * */ public class SubIntegerSumTask implements Runnable{ private List<Integer> subList; public SubIntegerSumTask(List<Integer> subList) { this.subList=subList; } public void run() { long subSum=0L; for (Integer i : subList) { subSum += i; } synchronized(CountListIntegerSum.this){//在CountListIntegerSum对象上同步 sum+=subSum; } try { barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处 } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+":Interrupted"); } catch (BrokenBarrierException e) { System.out.println(Thread.currentThread().getName()+":BrokenBarrier"); } System.out.println("分配给线程:"+Thread.currentThread().getName()+"那一部分List的整数和为:\tSubSum:"+subSum); } } }
有人可能对barrier=new CyclicBarrier(threadCounts+1);//创建的线程数和主线程main有点不解,不是采用的线程(任务)数是threadCounts个吗?怎么为CyclicBarrier设置的给定数量的线程参与者比我们要采用的线程数多一个呢?答案就是这个多出来的一个用于控制main主线程的,主线程也要等待,它要等待其他所有的线程完成才能输出sum值,这样才能保证sum值的正确性,如果main不等待的话,那么结果将是不可预料的。
/** * 计算List中所有整数的和测试类 * @author 飞雪无情 * @since 2010-7-12 */ public class CountListIntegerSumMain { /** * @param args */ public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); int threadCounts = 10;//采用的线程数 //生成的List数据 for (int i = 1; i <= 1000000; i++) { list.add(i); } CountListIntegerSum countListIntegerSum=new CountListIntegerSum(list,threadCounts); long sum=countListIntegerSum.getIntegerSum(); System.out.println("List中所有整数的和为:"+sum); } }
四:总结
本文主要通过一个淘宝的面试题为引子,介绍了并发的一点小知识,主要是介绍通过CyclicBarrier同步辅助器辅助多个并发任务共同完成一件工作。Java SE5的java.util.concurrent引入了大量的设计来解决并发问题,使用它们有助于我们编写更加简单而健壮的并发程序。
附mathfox提到的ExecutorService.invokeAll()方法的实现
这个不用自己控制等待,invokeAll执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。sdh5724也说用了同步,性能不好。这个去掉了同步,根据返回结果的 Future 列表相加就得到总和了。
/** * 使用ExecutorService的invokeAll方法计算 * @author 飞雪无情 * */ public class CountSumWithCallable { /** * @param args * @throws InterruptedException * @throws ExecutionException */ public static void main(String[] args) throws InterruptedException, ExecutionException { int threadCounts =19;//使用的线程数 long sum=0; ExecutorService exec=Executors.newFixedThreadPool(threadCounts); List<Callable<Long>> callList=new ArrayList<Callable<Long>>(); //生成很大的List List<Integer> list = new ArrayList<Integer>(); for (int i = 0; i <= 1000000; i++) { list.add(i); } int len=list.size()/threadCounts;//平均分割List //List中的数量没有线程数多(很少存在) if(len==0){ threadCounts=list.size();//采用一个线程处理List中的一个元素 len=list.size()/threadCounts;//重新平均分割List } for(int i=0;i<threadCounts;i++){ final List<Integer> subList; if(i==threadCounts-1){ subList=list.subList(i*len,list.size()); }else{ subList=list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1)); } //采用匿名内部类实现 callList.add(new Callable<Long>(){ public Long call() throws Exception { long subSum=0L; for(Integer i:subList){ subSum+=i; } System.out.println("分配给线程:"+Thread.currentThread().getName()+"那一部分List的整数和为:\tSubSum:"+subSum); return subSum; } }); } List<Future<Long>> futureList=exec.invokeAll(callList); for(Future<Long> future:futureList){ sum+=future.get(); } exec.shutdown(); System.out.println(sum); } }
一些感言
这篇文章是昨天夜里11点多写好的,我当时是在网上看到了这个题目,就做了一下分析,写了实现代码,由于水平有限,难免有bug,这里感谢xifo等人的指正。这些帖子从发表到现在不到24小时的时间里创造了近9000的浏览次数,回复近100,这是我没有想到的,javaeye很久没这么疯狂过啦。这不是因为我的算法多好,而是因为这个题目、这篇帖子所体现出的意义。大家在看完这篇帖子后不光指正错误,还对方案进行了改进,关键是思考,人的思维是无穷的,只要我们善于发掘,善于思考,总能想出一些意想不到的方案。
从算法看,或者从题目场景对比代码实现来看,或许不是一篇很好的帖子,但是我说这篇帖子是很有意义的,方案也是在很多场景适用,有时我们可以假设这不是计算和,而是把数据写到一个个的小文件里,或者是分割进行网络传输等等,都有一定的启发,特别是回帖中的讨论。
单说一下回帖,我建议进来的人尽量看完所有的回帖,因为这里是很多人集思广益的精华,这里有他们分析问题,解决问题的思路,还有每个人提到的解决方案,想想为什么能用?为什么不能用?为什么好?为什么不好?
我一直相信:讨论是解决问题、提高水平的最佳方式!
评论
125 楼
kakaluyi
2010-07-15
dennis_zane 写道
这种题目用java做真是太多代码了,用clojure只要几行:
(defn mysum [coll n]
(let [sub-colls (partition n n [0] coll)
result-coll (map #(future (reduce + 0 %)) sub-colls)]
(reduce #(+ %1 @%2) 0 result-coll)))
(defn mysum [coll n]
(let [sub-colls (partition n n [0] coll)
result-coll (map #(future (reduce + 0 %)) sub-colls)]
(reduce #(+ %1 @%2) 0 result-coll)))
传说中的map-redurce
124 楼
hardPass
2010-07-15
kakaluyi 写道
mercyblitz 写道
kakaluyi 写道
skzr.org 写道
使用Gedit编辑的,难免有语法错误,呵呵
我的一个解法:
我的一个解法:
public class MyWorkThread extends Thread { private static BigDecimal sum; private List<Integer> list; private int start, end; private long value; public static BigDecimal getSum() { return sum; } public static synchronized void addSum(long v) { if (sum == null) { sum = new BigDecimal(v); } else { sum.add(BigDecimal.valueOf(v)); } } public MyWorkThread(List<Integer> list, Integer start, Integer end) { this.list = list; this.start = start; this.end = end; } private void add(int v) { if (Long.MAX_VALUE - v > value) { value += v; } else { addSum(value); value = v; } } public void run() { for(int i = start; i < end; i++) add(list.get(i)); } public static void main(String[] args) throws InterruptedException { List<Integer> list = new ArrayList<Integer>(); int cpuCoreSize = 2; int len = list.size() / cpuCoreSize; int start = 0, end = len; for (;;) { end = start + len; if (end > list.size()) end = list.size(); new MyWorkThread(list, start, end).start(); start = end; if (start == list.size()) break; } [color=red]Thread.currentThread().join();[/color] System.out.println("和为:" + MyWorkThread.getSum()); } }
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
不会,只是会活锁。你可以在main方法里面试试。
经过验证,不是活锁,证明了我的担心,是个死锁
public static void main(String args[])
{
try {
Thread.currentThread().join();
System.out.println("ok");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ok永远打印不出来,主线程等待所有子线程结束的方法是不是酱紫地,前面一个同学说了一个方法
atominit方法才是正解
Thread.currentThread().join(); 莫非是传说中的永久sleep,必须等待别人来打断?
123 楼
410133062
2010-07-15
<div class="quote_title">dennis_zane 写道</div>
<div class="quote_div">这种题目用java做真是太多代码了,用clojure只要几行:<br><br>(defn mysum [coll n]<br> (let [sub-colls (partition n n [0] coll)<br> result-coll (map #(future (reduce + 0 %)) sub-colls)] <br> (reduce #(+ %1 @%2) 0 result-coll)))<br><br>
</div>
<p> </p>
<p>真够简单的,用erlang代码也很少</p>
<div class="quote_div">这种题目用java做真是太多代码了,用clojure只要几行:<br><br>(defn mysum [coll n]<br> (let [sub-colls (partition n n [0] coll)<br> result-coll (map #(future (reduce + 0 %)) sub-colls)] <br> (reduce #(+ %1 @%2) 0 result-coll)))<br><br>
</div>
<p> </p>
<p>真够简单的,用erlang代码也很少</p>
122 楼
dennis_zane
2010-07-15
这种题目用java做真是太多代码了,用clojure只要几行:
(defn mysum [coll n]
(let [sub-colls (partition n n [0] coll)
result-coll (map #(future (reduce + 0 %)) sub-colls)]
(reduce #(+ %1 @%2) 0 result-coll)))
(defn mysum [coll n]
(let [sub-colls (partition n n [0] coll)
result-coll (map #(future (reduce + 0 %)) sub-colls)]
(reduce #(+ %1 @%2) 0 result-coll)))
121 楼
mingjian01
2010-07-15
昨天刚刚看了concurrent包的ConcurrentHashMap的分析,也是使用分段来进行并发,读操作允许并发,修改操作加锁
120 楼
kakaluyi
2010-07-15
mercyblitz 写道
kakaluyi 写道
skzr.org 写道
使用Gedit编辑的,难免有语法错误,呵呵
我的一个解法:
我的一个解法:
public class MyWorkThread extends Thread { private static BigDecimal sum; private List<Integer> list; private int start, end; private long value; public static BigDecimal getSum() { return sum; } public static synchronized void addSum(long v) { if (sum == null) { sum = new BigDecimal(v); } else { sum.add(BigDecimal.valueOf(v)); } } public MyWorkThread(List<Integer> list, Integer start, Integer end) { this.list = list; this.start = start; this.end = end; } private void add(int v) { if (Long.MAX_VALUE - v > value) { value += v; } else { addSum(value); value = v; } } public void run() { for(int i = start; i < end; i++) add(list.get(i)); } public static void main(String[] args) throws InterruptedException { List<Integer> list = new ArrayList<Integer>(); int cpuCoreSize = 2; int len = list.size() / cpuCoreSize; int start = 0, end = len; for (;;) { end = start + len; if (end > list.size()) end = list.size(); new MyWorkThread(list, start, end).start(); start = end; if (start == list.size()) break; } [color=red]Thread.currentThread().join();[/color] System.out.println("和为:" + MyWorkThread.getSum()); } }
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
不会,只是会活锁。你可以在main方法里面试试。
经过验证,不是活锁,证明了我的担心,是个死锁
public static void main(String args[])
{
try {
Thread.currentThread().join();
System.out.println("ok");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ok永远打印不出来,主线程等待所有子线程结束的方法是不是酱紫地,前面一个同学说了一个方法
atominit方法才是正解
119 楼
chancelai
2010-07-15
啥时候能写出这样的代码啊
越挫越勇
越挫越勇
118 楼
mercyblitz
2010-07-14
kakaluyi 写道
skzr.org 写道
使用Gedit编辑的,难免有语法错误,呵呵
我的一个解法:
我的一个解法:
public class MyWorkThread extends Thread { private static BigDecimal sum; private List<Integer> list; private int start, end; private long value; public static BigDecimal getSum() { return sum; } public static synchronized void addSum(long v) { if (sum == null) { sum = new BigDecimal(v); } else { sum.add(BigDecimal.valueOf(v)); } } public MyWorkThread(List<Integer> list, Integer start, Integer end) { this.list = list; this.start = start; this.end = end; } private void add(int v) { if (Long.MAX_VALUE - v > value) { value += v; } else { addSum(value); value = v; } } public void run() { for(int i = start; i < end; i++) add(list.get(i)); } public static void main(String[] args) throws InterruptedException { List<Integer> list = new ArrayList<Integer>(); int cpuCoreSize = 2; int len = list.size() / cpuCoreSize; int start = 0, end = len; for (;;) { end = start + len; if (end > list.size()) end = list.size(); new MyWorkThread(list, start, end).start(); start = end; if (start == list.size()) break; } [color=red]Thread.currentThread().join();[/color] System.out.println("和为:" + MyWorkThread.getSum()); } }
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
不会,只是会活锁。你可以在main方法里面试试。
117 楼
kakaluyi
2010-07-14
skzr.org 写道
使用Gedit编辑的,难免有语法错误,呵呵
我的一个解法:
我的一个解法:
public class MyWorkThread extends Thread { private static BigDecimal sum; private List<Integer> list; private int start, end; private long value; public static BigDecimal getSum() { return sum; } public static synchronized void addSum(long v) { if (sum == null) { sum = new BigDecimal(v); } else { sum.add(BigDecimal.valueOf(v)); } } public MyWorkThread(List<Integer> list, Integer start, Integer end) { this.list = list; this.start = start; this.end = end; } private void add(int v) { if (Long.MAX_VALUE - v > value) { value += v; } else { addSum(value); value = v; } } public void run() { for(int i = start; i < end; i++) add(list.get(i)); } public static void main(String[] args) throws InterruptedException { List<Integer> list = new ArrayList<Integer>(); int cpuCoreSize = 2; int len = list.size() / cpuCoreSize; int start = 0, end = len; for (;;) { end = start + len; if (end > list.size()) end = list.size(); new MyWorkThread(list, start, end).start(); start = end; if (start == list.size()) break; } [color=red]Thread.currentThread().join();[/color] System.out.println("和为:" + MyWorkThread.getSum()); } }
52行Thread.currentThread().join();朋友有个地方不懂,这里Thread.currentThread()是主线程吧,那join()方法就是是主线程等待主线程执行完成,这不是抛出InterruptedException吗
116 楼
hardPass
2010-07-14
package com.wl.test.concurrent.semaphore; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; /** * * 有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果。 * * * 乍一看到题目“充分利用多核CPU”, * 以为会根据CPU的核心数,计算出比较合理的任务线程的数目。 * 就题目的计算目标来说,实际上讲的主线程等待任务线程完成, * 任务线程之间没有必要互相等待。 * 是不是可以考虑用信号来...... * 每次来看帖,都发现大家图画的很拉风…… * * 我也帖个代码,虽然来晚了,大家不一定能看到…… 加锁计数,原来放在任务线程中的,现在那到外面来了 * * @author HardPass * */ public class BigListSumWithSemaphore { private List<Integer> list = new ArrayList<Integer>(); private AtomicLong sum = new AtomicLong(0L); // 结果 private int threadCounts = 2 * Runtime.getRuntime().availableProcessors() + 1; // 任务线程数 private List<Runnable> tasks = new ArrayList<Runnable>(); // 任务线程 SemaphoreLock lock = new SemaphoreLock(threadCounts);// 初始化锁计数 改变的地方 public static void main(String[] args) { //测试100次 for (int i = 0; i < 100; i++) { long l = new BigListSumWithSemaphore().test(); if (l!=500000500000L) System.out.println("___________________________________________________________________________________________________________________________________"); } } public long test() { init(); System.out.println("---" + threadCounts); ExecutorService exec = Executors.newFixedThreadPool(threadCounts); //线程池 for(Runnable task : tasks){ exec.execute(task); } lock.lockHere(); // 此处尝试wait exec.shutdown(); System.out.println("List中所有整数的和为:" + sum); return sum.get(); } private void init() { for (int i = 1; i <= 1000000; i++) { list.add(i); } int len = list.size() / threadCounts; int i = 0; for (; i < threadCounts - 1; i++) { tasks.add(new Task(list.subList(i * len, (i + 1) * len))); } tasks.add(new Task(list.subList(i * len, list.size()))); } private class Task implements Runnable { private List<Integer> subList; public Task(List<Integer> subList) { this.subList = subList; } @Override public void run() { //lock.lockThere(); // 增加锁的计数 long subSum = 0L; for (Integer i : subList) { subSum += i; } sum.addAndGet(subSum); System.out.println("分配给线程:" + Thread.currentThread().getName() + "那一部分List的整数和为:\tSubSum:" + subSum); lock.release(); // 释放一个锁的计数 } } } /** * * 信号量锁 * * 此Semaphore非java.util.concurrent.Semaphore * * @author HardPass * */ class SemaphoreLock { private int count = 0; // 信号量 SemaphoreLock(int count){ this.count = count; } /** * 信号量大于0的时候 wait * 这是不是传说中的可重入? */ public synchronized void lockHere() { while (count > 0) { try { wait(); } catch (InterruptedException e) { } } } public synchronized void lockThere() { count++; } public synchronized void release() { count--; if(count==0){ notify(); } } }
115 楼
yangyi
2010-07-14
其实性能还可以再提高的,Cyclicbarrier运算完毕后都要await是很讨厌的。
应该使用CompletionService,然后这样
应该使用CompletionService,然后这样
long count = 0L; for(int i=0;i<segCount;i++){ count += cs.take().get(); }
114 楼
hardPass
2010-07-14
dien 写道
hardPass 写道
package com.wl.test.concurrent.semaphore; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; /** * * 有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果。 * * * 乍一看到题目“充分利用多核CPU”, * 以为会根据CPU的核心数,计算出比较合理的任务线程的数目。 * 就题目的计算目标来说,实际上讲的主线程等待任务线程完成, * 任务线程之间没有必要互相等待。 * 是不是可以考虑用信号来...... * 每次来看帖,都发现大家图画的很拉风…… * * 我也帖个代码,虽然来晚了,大家不一定能看到…… * * @author HardPass * */ public class BigListSumWithSemaphore { private List<Integer> list = new ArrayList<Integer>(); private AtomicLong sum = new AtomicLong(0L); // 结果 private int threadCounts = 2 * Runtime.getRuntime().availableProcessors() + 1; // 任务线程数 private List<Runnable> tasks = new ArrayList<Runnable>(); // 任务线程 SemaphoreLock lock = new SemaphoreLock(); public static void main(String[] args) { new BigListSumWithSemaphore().test(); } public void test() { init(); ExecutorService exec = Executors.newFixedThreadPool(threadCounts); //线程池 for(Runnable task : tasks){ exec.execute(task); } lock.lockHere(); // 此处尝试wait exec.shutdown(); System.out.println("List中所有整数的和为:" + sum); } private void init() { for (int i = 1; i <= 1000000; i++) { list.add(i); } int len = list.size() / threadCounts; int i = 0; for (; i < threadCounts - 1; i++) { tasks.add(new Task(list.subList(i * len, (i + 1) * len))); } tasks.add(new Task(list.subList(i * len, list.size()))); } private class Task implements Runnable { private List<Integer> subList; public Task(List<Integer> subList) { this.subList = subList; } @Override public void run() { lock.lockThere(); // 增加锁的计数 long subSum = 0L; for (Integer i : subList) { subSum += i; } sum.addAndGet(subSum); System.out.println("分配给线程:" + Thread.currentThread().getName() + "那一部分List的整数和为:\tSubSum:" + subSum); lock.release(); // 释放一个锁的计数 } } } /** * * 信号量锁 * * 此Semaphore非java.util.concurrent.Semaphore * * @author HardPass * */ class SemaphoreLock { private int count = 0; // 信号量 /** * 信号量大于0的时候 wait * 这是不是传说中的可重入? */ public synchronized void lockHere() { while (count > 0) { try { wait(); } catch (InterruptedException e) { } } } public synchronized void lockThere() { count++; } public synchronized void release() { count--; if(count==0){ notify(); } } }
对这个进行100次测试,发现有时候算出来的答案不正确,最后一个线程的结果没有累加进去,,,
5分配给线程:pool-1-thread-3那一部分List的整数和为: SubSum:100000100000
5分配给线程:pool-1-thread-1那一部分List的整数和为: SubSum:20000100000
5分配给线程:pool-1-thread-2那一部分List的整数和为: SubSum:60000100000
5分配给线程:pool-1-thread-4那一部分List的整数和为: SubSum:140000100000
===List中所有整数的和为:320000400000threadCounts:5
5分配给线程:pool-1-thread-5那一部分List的整数和为: SubSum:180000100000
代码:
sum.addAndGet(subSum);
System.out.println("分配给线程:" + Thread.currentThread().getName()
+ "那一部分List的整数和为:\tSubSum:" + subSum);
lock.release(); // 释放一个锁的计数
这个存在问题的?
代码理论由上往下执行,这个什么时候可能存在这样问题?(以前看单例双重检查加锁,new语句开辟内存区,但还未初始化数据,类似的?)
没这么复杂
应该是主线程执行太快,任务线程还没来得及锁导致的。
这个东西本来是想模拟CountDownLatch的
仔细想了下,这个设计还会有很大的问题,如果其中某个或者某些任务线程起的太快,并且完成的也快,会导致锁计数很快增加并很快减为0,并且在这个时候,还有一些任务线程还没有启动……
解决方法也不是没有,可以将加锁放在主线程,任务线程只release^
还是用CountDownLatch 比较稳妥
ExecuteService invokeall callable 也是推荐的方法
113 楼
bluethink
2010-07-14
很有意义的帖子,看来JE里真是卧虎藏龙啊,呵呵
112 楼
whywen_MoJian
2010-07-14
不能不顶。。。。
111 楼
mercyblitz
2010-07-14
skzr.org 写道
使用Gedit编辑的,难免有语法错误,呵呵
我的一个解法:
我的一个解法:
public class MyWorkThread extends Thread { private static BigDecimal sum; private List<Integer> list; private int start, end; private long value; public static BigDecimal getSum() { return sum; } public static synchronized void addSum(long v) { if (sum == null) { sum = new BigDecimal(v); } else { sum.add(BigDecimal.valueOf(v)); } } public MyWorkThread(List<Integer> list, Integer start, Integer end) { this.list = list; this.start = start; this.end = end; } private void add(int v) { if (Long.MAX_VALUE - v > value) { value += v; } else { addSum(value); value = v; } } public void run() { for(int i = start; i < end; i++) add(list.get(i)); } public static void main(String[] args) throws InterruptedException { List<Integer> list = new ArrayList<Integer>(); int cpuCoreSize = 2; int len = list.size() / cpuCoreSize; int start = 0, end = len; for (;;) { end = start + len; if (end > list.size()) end = list.size(); new MyWorkThread(list, start, end).start(); start = end; if (start == list.size()) break; } Thread.currentThread().join(); System.out.println("和为:" + MyWorkThread.getSum()); } }
这个做法和楼主的差不多,在Java Collection,List#sublist方法,会创建一个新的java.util.SubList和它的子类,也是利用的偏移量来做的。
这里完全没有必要使用同步,使用AtomicLong就行了,如果大List是一个ArrayList的话,一个足够了,如果是LinkedList的实现,多个AtomicLong就行了。这里顺序不重要,利用Atomic*的内存CAS操作即可。
110 楼
skzr.org
2010-07-14
使用Gedit编辑的,难免有语法错误,呵呵
我的一个解法:
我的一个解法:
public class MyWorkThread extends Thread { private static BigDecimal sum; private List<Integer> list; private int start, end; private long value; public static BigDecimal getSum() { return sum; } public static synchronized void addSum(long v) { if (sum == null) { sum = new BigDecimal(v); } else { sum.add(BigDecimal.valueOf(v)); } } public MyWorkThread(List<Integer> list, Integer start, Integer end) { this.list = list; this.start = start; this.end = end; } private void add(int v) { if (Long.MAX_VALUE - v > value) { value += v; } else { addSum(value); value = v; } } public void run() { for(int i = start; i < end; i++) add(list.get(i)); } public static void main(String[] args) throws InterruptedException { List<Integer> list = new ArrayList<Integer>(); int cpuCoreSize = 2; int len = list.size() / cpuCoreSize; int start = 0, end = len; for (;;) { end = start + len; if (end > list.size()) end = list.size(); new MyWorkThread(list, start, end).start(); start = end; if (start == list.size()) break; } Thread.currentThread().join(); System.out.println("和为:" + MyWorkThread.getSum()); } }
109 楼
dien
2010-07-14
hardPass 写道
package com.wl.test.concurrent.semaphore; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; /** * * 有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果。 * * * 乍一看到题目“充分利用多核CPU”, * 以为会根据CPU的核心数,计算出比较合理的任务线程的数目。 * 就题目的计算目标来说,实际上讲的主线程等待任务线程完成, * 任务线程之间没有必要互相等待。 * 是不是可以考虑用信号来...... * 每次来看帖,都发现大家图画的很拉风…… * * 我也帖个代码,虽然来晚了,大家不一定能看到…… * * @author HardPass * */ public class BigListSumWithSemaphore { private List<Integer> list = new ArrayList<Integer>(); private AtomicLong sum = new AtomicLong(0L); // 结果 private int threadCounts = 2 * Runtime.getRuntime().availableProcessors() + 1; // 任务线程数 private List<Runnable> tasks = new ArrayList<Runnable>(); // 任务线程 SemaphoreLock lock = new SemaphoreLock(); public static void main(String[] args) { new BigListSumWithSemaphore().test(); } public void test() { init(); ExecutorService exec = Executors.newFixedThreadPool(threadCounts); //线程池 for(Runnable task : tasks){ exec.execute(task); } lock.lockHere(); // 此处尝试wait exec.shutdown(); System.out.println("List中所有整数的和为:" + sum); } private void init() { for (int i = 1; i <= 1000000; i++) { list.add(i); } int len = list.size() / threadCounts; int i = 0; for (; i < threadCounts - 1; i++) { tasks.add(new Task(list.subList(i * len, (i + 1) * len))); } tasks.add(new Task(list.subList(i * len, list.size()))); } private class Task implements Runnable { private List<Integer> subList; public Task(List<Integer> subList) { this.subList = subList; } @Override public void run() { lock.lockThere(); // 增加锁的计数 long subSum = 0L; for (Integer i : subList) { subSum += i; } sum.addAndGet(subSum); System.out.println("分配给线程:" + Thread.currentThread().getName() + "那一部分List的整数和为:\tSubSum:" + subSum); lock.release(); // 释放一个锁的计数 } } } /** * * 信号量锁 * * 此Semaphore非java.util.concurrent.Semaphore * * @author HardPass * */ class SemaphoreLock { private int count = 0; // 信号量 /** * 信号量大于0的时候 wait * 这是不是传说中的可重入? */ public synchronized void lockHere() { while (count > 0) { try { wait(); } catch (InterruptedException e) { } } } public synchronized void lockThere() { count++; } public synchronized void release() { count--; if(count==0){ notify(); } } }
对这个进行100次测试,发现有时候算出来的答案不正确,最后一个线程的结果没有累加进去,,,
5分配给线程:pool-1-thread-3那一部分List的整数和为: SubSum:100000100000
5分配给线程:pool-1-thread-1那一部分List的整数和为: SubSum:20000100000
5分配给线程:pool-1-thread-2那一部分List的整数和为: SubSum:60000100000
5分配给线程:pool-1-thread-4那一部分List的整数和为: SubSum:140000100000
===List中所有整数的和为:320000400000threadCounts:5
5分配给线程:pool-1-thread-5那一部分List的整数和为: SubSum:180000100000
代码:
sum.addAndGet(subSum);
System.out.println("分配给线程:" + Thread.currentThread().getName()
+ "那一部分List的整数和为:\tSubSum:" + subSum);
lock.release(); // 释放一个锁的计数
这个存在问题的?
代码理论由上往下执行,这个什么时候可能存在这样问题?(以前看单例双重检查加锁,new语句开辟内存区,但还未初始化数据,类似的?)
108 楼
czpsailer
2010-07-14
从这个帖子和回复中,受益良多呀
107 楼
sunney2010
2010-07-14
精典中的精典!学习中
106 楼
飞雪无情
2010-07-14
Joo 写道
为什么要先分好呢? 而且这里线程数是否可以动态变化?
我意思让线程自己去BigList中去取数, 加完再取, 这样无论多少个线程, 也不会出现等待的情况, 直道再取不出数了即可.
又或者, 从bigList中每次取两个, 相加完成后放回bigList(可以把list构造成栈,FIFO), 这样无论多少线程,都好用了
我意思让线程自己去BigList中去取数, 加完再取, 这样无论多少个线程, 也不会出现等待的情况, 直道再取不出数了即可.
又或者, 从bigList中每次取两个, 相加完成后放回bigList(可以把list构造成栈,FIFO), 这样无论多少线程,都好用了
你说的意思应该是阻塞队列的那种情况,不过这样也有等待,当其中一个线程去取值的时候,其他的线程就要等着。
分割是为了分配任务,就像大家都敢一个活,你做这点,我做这点,咱俩互不影响,都干完了活也就完了。
相关推荐
2. **丰富的库支持**:Python 拥有庞大的标准库和第三方库,几乎涵盖了所有应用领域,如网络编程、图形用户界面、科学计算等。 3. **跨平台性**:Python 可以运行在多种操作系统上,如 Windows、Linux 和 macOS 等。...
### Delphi 面试题知识点解析 #### 一、基础知识题解析 1. **Delphi 是什么?它主要应用于什么领域?** - **Delphi** 是一种基于 Object Pascal 的集成开发环境(IDE),主要用于 Windows 平台上的应用程序开发。...
- **作用**: 由于GIL的存在,在多线程环境下,即使CPU有多核也无法充分利用多核的优势,因为同一进程中只有一个线程可以执行Python字节码。 - **影响**: 对于I/O密集型的应用,GIL的影响较小,但对于CPU密集型任务...
根据给定文件的信息,我们可以总结出一系列与Python相关的面试题及其答案。这些问题涵盖了Python的基本语法、数据类型、高级特性等多个方面。接下来,我们将详细解析这些知识点。 ### 模块和包的区别 在Python中,...
Python也提供了多线程和多进程支持,以便更好地利用多核处理器。 6. 全局解释器锁(GIL) Python中的全局解释器锁(GIL)是为了简化内存管理而设计的,它限制了同一时刻只有一个线程可以执行Python字节码。这意味着...
- 利用多进程可以充分利用多核CPU资源。 7. **深拷贝与浅拷贝** - 深拷贝创建一个全新的对象,而浅拷贝仅复制对象的引用。 - 当原对象被修改时,深拷贝的对象不受影响,而浅拷贝的对象可能会受到影响。 8. **is...
### Python面试题详解 #### 1. Python的函数参数传递 在Python中,函数参数的传递遵循“传值”而非“传引用”的原则。当传递不可变数据类型(如整数、字符串等)时,实际上是将该数据类型的值复制一份传递给函数;...
51. Java 8的ConcurrentHashMap弃用分段锁是因为分段锁在多核CPU环境下性能瓶颈,改为使用CAS和Node链表实现。 52. ConcurrentHashMap使用synchronized而非ReentrantLock是因为减少锁粒度,提高并发性能。 53. ...
通过在同一台机器上启动多个Redis实例,可以充分利用多核资源。 3. **Redis的性能优势**: - **速度**:由于数据存储在内存中,Redis的读写速度非常快,接近O(1)的时间复杂度。 - **丰富的数据类型**:支持多种...
这限制了多核CPU下的并行计算能力,但在IO密集型任务中,多线程仍然能提高效率。 - 使用`threading`库可以创建线程,但需要注意GIL的存在可能导致多线程不如预期中的并行。 以上是Python面试中常见的知识点,理解...