0

我正在玩线程,我不明白为什么这不像我想的那样工作。

我正在尝试使用线程计算总和,并期望线程池在我打印出结果时等待所有任务完成(由于 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
4

2 回答 2

2

您需要在主对象实例上同步。我在int下面使用,整数也可以工作(需要显式初始化为零)。

这是工作代码

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;
            }
        }
    }
}

ps 忙等待是一种要避免的反模式(从邻居的答案中复制以完成并了解这一重要事情,请参阅评论)

于 2013-04-18T14:23:00.630 回答
1

你有很多问题。

  1. 您的代码不是线程安全的
  2. 忙碌等待是一种需要避免的反模式。

1.是什么意思?

假设我们有两个线程,A 和 B。

  1. A 读sumtmp1
  2. B 读sumtmp1
  3. Asum增加到 2
  4. A 写sum为 2
  5. Bsum增加到 2
  6. B 写sum为 2

所以我们在两个增量后得到 2。不完全对。

现在你可能会说“但我用过synchronized,这不应该发生”。好吧,你没有。

当你创建你的Adding实例时,你new每个人。您有 1000 个单独的Adding实例。

当您synchronized(this)在当前实例上同步时,而不是在所有Adding. 所以你的同步块什么都不做。

现在,简单的解决方案是使用synchronized(Adding.class).

synchronized(Adding.class)将使代码块在所有Adding实例中正确同步。

好的解决方案是使用 anAtmoicInteger而不是 an ,因为它会Integer以原子方式递增,并且专为此类任务而设计。

现在进入2。

你有一个while(thing){}循环,这基本上像疯狂的测试一样每毫秒运行数千次直到thingis true。这是对 CPU 周期的巨大浪费。AnExecutorService有一个特殊的阻塞方法,它一直等到它关闭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在这种情况下不要使用 a ,因为你有 1000 s 被提交,这将产生比你的 CPURunnable多得多的 s 。Thread我建议使用newFixedThreadPool合理数量的Threads。

我什至不打算使用int文字Integer以及为什么new Integer()不需要。

于 2013-04-18T14:17:48.037 回答