我在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上执行它时,每次运行时它都会给出不同的输出值。想知道是否有人知道它为什么会这样。非常感谢。
答案 0 :(得分: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
需要Callable
而不是Runnable
的列表,因此任务将为Callable
,但这不会影响此代码的语义
答案 1 :(得分:2)
基本上,线程main
在所有之前调用shutdownNow
已完成的任务(即使没有调用shutdownNow
,您仍然看不到{{ 1}},因为你在执行者完成之前仍在查询2000
。
你真的想要阻止,直到你的执行者完成或发生超时:
AtomicInteger
如果您仔细查看帖子的作者,那么这些文章来自定义:
executor.shutdown();
executor.awaitTermination(100, TimeUnit.MILLISECONDS);
答案 2 :(得分:1)
shutdownNow
的JavaDoc说:
尝试停止所有正在执行的任务,停止处理 等待任务,并返回正在等待的任务列表 执行。
此方法不等待主动执行任务终止。 使用awaitTermination来做到这一点。
这样做不等待您提交完成的所有任务,所以只需获取设法运行的线程的结果。
要关闭服务并等待所有内容完成,请将shutdownNow
替换为:
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
(您需要从某处InterruptedException
抓住awaitTermination
。