我在玩线程,我不明白为什么这没有按照我的想法工作。
我正在尝试使用线程来计算总和,并期望线程池在我打印出结果时等待所有任务完成(由于shutdown()调用和isTerminated()检查)。
我在这里想念什么?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test5 {
private Integer sum= new Integer(0);
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
Test5 obj = new Test5();
for(int i=0; i<1000; i++){
pool.execute(obj.new Adding());
}
pool.shutdown();
while(!pool.isTerminated()) {
//could be empty loop...
System.out.println(" Is it done? : " + pool.isTerminated());
}
System.out.println(" Is it done? : " + pool.isTerminated());
System.out.println("Sum is " + obj.sum);
}
class Adding implements Runnable {
public void run() {
synchronized(this) {
int tmp = sum;
tmp+=1;
sum=new Integer(tmp);
}
}
}
}
虽然确实取得了不错的结果,但我也得到了这样的输出:
Is it done? : true
Sum is 983
最佳答案
您有许多问题。
您的代码不是线程安全的
繁忙的等待是要避免的反模式。
1.是什么意思?
假设我们有两个线程,A和B。
A将sum
读入tmp
为1
B将sum
读入tmp
为1sum
递增至2
A将sum
写为2
B将sum
递增至2
B将sum
写为2
因此,我们在两个增量后得到2。不太正确。
现在您可能会说“但是我已经使用过synchronized
,这应该不会发生”。好吧,您还没有。
创建Adding
实例时,您每个new
。您有1000个单独的Adding
实例。synchronized(this)
时,将在当前实例上同步,而不是在所有Adding
上同步。因此,您的同步块不执行任何操作。
现在,简单的解决方案是使用synchronized(Adding.class)
。synchronized(Adding.class)
将使代码块在所有Adding
实例之间正确同步。
好的解决方案是使用AtmoicInteger
而不是Integer
,因为它原子地递增,并且专门用于此类任务。
现在到2。
您有一个while(thing){}
循环,这基本上像疯狂测试一样运行线程数千毫秒,直到thing
为true
。这是CPU周期的巨大浪费。 ExecutorService
具有一个特殊的阻止方法,该方法一直等待直到它关闭后才开始运行。
这是一个例子:
static final AtomicInteger sum = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
pool.execute(new Adding());
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS);
System.out.println(" Is it done? : " + pool.isTerminated());
System.out.println("Sum is " + sum);
}
static class Adding implements Runnable {
public void run() {
sum.addAndGet(1);
}
}
我还建议不要在这种情况下使用
awaitTermination
,因为您要提交1000个cachedThreadPool
,并且生成的Runnable
比您拥有的CPU多得多。我建议将Thread
与合理数量的newFixedThreadPool
s一起使用。我什至不打算使用
Thread
文字和int
以及为什么不需要Integer
的原因。