Multithreading Java8:运行线程示例的问题
我在winterbe.com上看到了下面的例子,它演示了原子变量的使用Multithreading Java8:运行线程示例的问题,multithreading,java-8,thread-safety,atomic,atomicinteger,Multithreading,Java 8,Thread Safety,Atomic,Atomicinteger,我在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 = n
// 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是如何从线程安全场景中推导出来的。然而,当我试图在EclipseIDE上执行它时,每次运行时它都会给出不同的输出值。我想看看是否有人知道它为什么会这样。非常感谢。关闭现在的JavaDoc说: 尝试停止所有正在执行的任务,停止处理 等待的任务,并返回等待的任务列表 执行 此方法不会等待主动执行的任务终止。 使用此选项可以完成此操作 因此,这不会等待您提交的所有任务完成,因此只需获取成功运行的线程的结果即可 要关闭服务并等待一切完成,请将
shutdownNow
替换为以下内容:
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
(您需要从
等待终止
的某个地方捕获中断异常
。基本上,线程main
在执行所有任务之前正在调用shutdownow
(即使不调用shutdownNow
您仍然看不到2000
,因为您仍然在执行器完成之前查询AtomicInteger
)
您确实希望阻止,直到执行器完成或超时:
executor.shutdown();
executor.awaitTermination(100, TimeUnit.MILLISECONDS);
如果你仔细阅读这篇文章的作者,你会发现:
public static void stop(ExecutorService executor) {
try {
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
}
....
正如其他人所说,
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
是获取相同元素的列表
的最简单方法,它甚至不需要存储来保存那个数量的引用
由于
invokeAll
需要一个Callable
s列表,而不是Runnable
s列表,因此任务将是Callable
s,但这不会影响此代码的语义。@Holger编辑,谢谢。我想用invokeAll
发布一些东西,只是想不出如何很好地完成它。我也会这样做假设List List=Collections.nCopies(1000,()->atomicInt.updateAndGet(n->n+2))
将需要强制转换,因为它也可能是供应商
,但我猜目标类型也会被考虑在内(因为java-8对吗?)因此不需要强制转换,目标类型告诉它,否则,它可以是任何东西,而不仅仅是供应商
或可调用的
。没有目标类型,lambda几乎无法使用,所以很明显,为什么在Java 8中添加了它……感谢您的解释!