2

我在 winterbe.com 上遇到了以下示例,该示例演示了原子变量的使用。

// From http://winterbe.com/posts/2015/05/22/java8-concurrency-tutorial-atomic-concurrent-map-examples/
public class Test_AtomicInteger {
  public static void main(String[] args) {
      AtomicInteger atomicInt = new AtomicInteger(0);

      ExecutorService executor = Executors.newFixedThreadPool(2);

      IntStream.range(0, 1000)
          .forEach(i -> {
              Runnable task = () ->
                  atomicInt.updateAndGet(n -> n + 2);
              executor.submit(task);
          });

      executor.shutdownNow();

      System.out.println(atomicInt.get());    // => 2000
  }
}

了解如何从线程安全场景推导出预期值 2000。但是,当我尝试在 Eclipse IDE 上执行它时,每次运行它都会给出不同的输出值。想看看是否有人知道它为什么会这样。非常感谢。

4

3 回答 3

3

正如其他人所说,shutdownNow()这是不合适的,因为它可能会导致排队的任务被放弃,同时不等待当前正在运行的任务完成。

正确的序列后面会shutdown()跟着awaitTermination,但是,您可以更简单地执行相同的操作:

AtomicInteger atomicInt = new AtomicInteger(0);
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.invokeAll(Collections.nCopies(1000, () -> atomicInt.updateAndGet(n -> n + 2)));
System.out.println(atomicInt.get());    // => 2000
executor.shutdown(); // only for cleanup

在这里,invokeAll会调用所有的任务,所有的任务都可能并发运行,并等待所有任务的完成。执行器甚至不需要关闭,但可以重新用于其他任务,但是一旦不再需要它就应该关闭它,以清理底层资源。

Collections.nCopies是获取List相同元素的最简单方法,甚至不需要存储来保存那么多引用。

由于invokeAll需要 s 的列表Callable而不是Runnables,因此任务将是Callables,但这不会影响此代码的语义。

于 2018-01-23T11:10:55.630 回答
2

基本上,线程在所有执行的任务完成之前main调用(即使没有调用你仍然看不到,因为你在执行程序完成之前查询仍然)。shutdownNowshutdownNow2000AtomicInteger

您真的想阻止,直到您的执行程序完成或发生超时:

executor.shutdown();
executor.awaitTermination(100, TimeUnit.MILLISECONDS);

如果你仔细看一下这篇文章的作者定义:

 public static void stop(ExecutorService executor) {
    try {
        executor.shutdown();
        executor.awaitTermination(60, TimeUnit.SECONDS);
    }

    ....
于 2018-01-23T10:25:17.063 回答
1

JavaDocshutdownNow说:

尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。

此方法不等待主动执行的任务终止。使用 awaitTermination 来做到这一点。

因此,这不会等待您提交的所有任务完成,因此只需获取确实成功运行的线程的结果。

要关闭服务并等待一切完成,请使用以下内容替换shutdownNow

executor.shutdown();

executor.awaitTermination(10, TimeUnit.SECONDS);

(你需要InterruptedExceptionawaitTermination某个地方赶上)。

于 2018-01-23T10:06:39.283 回答