cachedThreadPool没有像我预期的那样工作

时间:2013-04-18 14:05:08

标签: java multithreading concurrency threadpool executorservice

我正在玩线程,我不明白为什么这不符合我的想法。

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

2 个答案:

答案 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. 您的代码不是线程安全的
  2. 忙碌的等待是一种需要避免的反模式。
  3. 我的意思是1。?

    假设我们有两个线程,A&amp;乙

    1. A将sum读入tmp作为1
    2. B将sum读入tmp作为1
    3. 增量sum至2
    4. A将sum写为2
    5. B将sum增加到2
    6. B将sum写为2
    7. 所以我们在两个增量之后得到2。不太对劲。

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

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

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

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

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

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

      现在上2。

      你有一个while(thing){}循环,这基本上就像疯狂测试一样,每毫秒一千次,直到thingtrue。这是一个巨大的浪费的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}}。