【问题标题】:Java: Using ExecutorService for concurrencyJava:使用 ExecutorService 进行并发
【发布时间】:2015-08-01 09:39:50
【问题描述】:

我想并行执行一些任务,所以我在 Java 中搜索多线程,并找到了这个类,ExecutorService。我尝试了一个简单的例子,但我不确定它是否并行运行。

long startTime = System.currentTimeMillis();
List<Callable<String>> callables = new ArrayList<Callable<String>>();
ExecutorService executor = Executors.newCachedThreadPool();
callables.add(new Callable<String>() {
        public String call() {
            for (int i=0; i<100000; i++) {
                System.out.println("i "+i);
            }
            return "Task 1";
        }
    }
);
        
callables.add(new Callable<String>() {
        public String call() {
            for (int j=0; j<100000; j++) {
                System.out.println("j "+j);
            }
            return "Task 2";
        }
    }
);
        
callables.add(new Callable<String>() {
     public String call() {
          for (int k=0; k<100000; k++) {
              System.out.println("k "+k);
          } 
          return "Task 3";
      }
  }
);
try {
    List<Future<String>> futureLst = executor.invokeAll(callables);
    for (Future future : futureLst) {
        try {
            System.out.println(future.get());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
executor.shutdown();
System.out.println("Time Taken - "+ (System.currentTimeMillis() - startTime));

上面的代码可以正常工作并打印计数并返回字符串。上述程序执行时间为“3229”毫秒。

我把上面的程序改写如下,

long startTime = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
    System.out.println("i "+i);
}
for (int j=0; j<100000; j++) {
    System.out.println("j "+j);
}
for (int k=0; k<100000; k++) {
    System.out.println("k "+k);
}
System.out.println("Time Taken - "+ (System.currentTimeMillis() - startTime));

这个程序花费的时间是“3104”毫秒,比并行执行编码的时间要短。

那么我在第一个程序中做错了吗?我的第一个程序是否并行运行任务,因为我看到没有线程花费的时间更少?

【问题讨论】:

  • 尝试使用 Runnable 而不是 Callable。

标签: java multithreading parallel-processing


【解决方案1】:

您的任务主要做的是使用System.out.println 调用(即PrintStream.println 方法)写入标准输出。它最终调用 PrintStream.write 方法,该方法的主体是同步的。因此,您只是将时间浪费在创建线程和同步开销上。由于PrintStream本质上是顺序的,它不能并行输出,所以当单个流正在写入时,其他人只是在等待它。某些操作可以并行化,例如您的 for 循环以及字符串和数字的串联,但它们比输出到标准输出要快得多。

要从并发中获得额外的性能,您应该避免使用共享的顺序资源。在您的情况下,它是标准输出流。

【讨论】:

    【解决方案2】:

    第一个是顺序的,第二个是并行的——没关系。

    看起来运行时间主要由System.out.printlncall 中的慢速控制台写入操作组成(请参阅Why is System.out.println so slow?)。

    如果您改为写入文件,则应该观察到不同的行为(以及并行版本加速)。

    【讨论】:

      【解决方案3】:

      您的代码是正确的。结果是由于计算太简单,运行太快。大部分运行时间取决于控制台输出的速度,因此多线程的好处无法弥补设置和关闭线程池的开销。此外,try-catch 设置还增加了一点开销(非常小,但足以在这个简单的场景中混淆结果)。 --> 您可以尝试增加循环大小(提示:使用静态变量 MAX_LOOP 以加快测试速度),您会看到不同的结果。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-23
        相关资源
        最近更新 更多