我在玩线程,我不明白为什么这没有按照我的想法工作。

我正在尝试使用线程来计算总和,并期望线程池在我打印出结果时等待所有任务完成(由于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为1
sum递增至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){}循环,这基本上像疯狂测试一样运行线程数千毫秒,直到thingtrue。这是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的原因。

10-06 11:05