我正在玩线程,我不明白为什么这不符合我的想法。
我正在尝试使用线程计算总和,并期望线程池在打印结果时等待所有任务完成(由于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
答案 0 :(得分:2)
您需要在主对象实例上进行同步。我在下面使用int
,Integer也会工作(需要明确地初始化为零)。
这是工作代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AppThreadsSum {
int sum;
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
AppThreadsSum app = new AppThreadsSum();
for (int i = 0; i < 1000; i++) {
pool.execute(app.new Adding());
}
pool.shutdown();
while (!pool.isTerminated()) {
System.out.println(" Is it done? : " + pool.isTerminated());
}
System.out.println(" Is it done? : " + pool.isTerminated());
System.out.println("Sum is " + app.sum);
}
class Adding implements Runnable {
public void run() {
synchronized (AppThreadsSum.this) {
sum += 1;
}
}
}
}
P.S。繁忙的等待是一种需要避免的反模式(从邻居的答案中复制完成并意识到这一重要的事情,请参阅评论)
答案 1 :(得分:1)
你有很多问题。
我的意思是1。?
假设我们有两个线程,A&amp;乙
sum
读入tmp
作为1 sum
读入tmp
作为1 sum
至2 sum
写为2 sum
增加到2 sum
写为2 所以我们在两个增量之后得到2。不太对劲。
现在你可以说“但我使用过synchronized
,这不应该发生”。好吧,你还没有。
当您创建Adding
个实例时,每个new
个实例。您有1000个单独的Adding
个实例。
当您synchronized(this)
同步当前实例时,所有Adding
中的不。所以你的synchronized块什么都不做。
现在,简单解决方案将使用synchronized(Adding.class)
。
synchronized(Adding.class)
将使代码块在所有Adding
个实例中正确同步。
好的解决方案是使用AtmoicInteger
而不是Integer
,因为这会以原子方式递增,并且专为此类任务而设计。
现在上2。
你有一个while(thing){}
循环,这基本上就像疯狂测试一样,每毫秒一千次,直到thing
为true
。这是一个巨大的浪费的CPU周期。 ExecutorService
有一个特殊的阻塞方法,等待它关闭,awaitTermination
。
以下是一个例子:
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);
}
}
我还建议在这种情况下不要使用cachedThreadPool
因为你提交了1000 Runnable
,这将产生比你拥有的CPU Thread
多得多的newFixedThreadPool
。我建议使用Thread
使用合理的int
s。
我甚至不打算使用Integer
文字和new Integer()
以及为什么不需要{{1}}。