我对多线程非常了解,但我认为我或多或少都有了整体想法。我正在尝试填充矩阵多线程,但我的代码显然不是线程安全的,我的矩阵中有重复的列,当矩阵定期填充时不是这种情况。下面是一个示例代码块。请注意,reader是一个Scanner对象,someOperationOnText(someText)返回一个int [100]对象。
int[][] mat = new int[100][100];
ExecutorService threadPool = Executors.newFixedThreadPool(8);
for (int i = 0; i < 100; i++) {
Set<Integer>someText = new HashSet<>(reader.next());
int lineIndex = i;
threadPool.submit(() -> mat[lineIndex] = someOperationOnText(someText);
}
你认为这不是线程安全的原因吗?我似乎无法理解它,因为读取是在线程池外完成的,我认为它不会有风险。
非常感谢任何调试技巧! GRTS
答案 0 :(得分:1)
在submit
调用和执行者线程池中的线程执行lambda之间有一个发生在之前。 (参见javadoc:“记忆一致性效应”)。这意味着lambda将看到someText
,mat
和lineIndex
的正确值。
唯一对此不是线程安全的是使用主线程中mat
中的值的(隐含)代码。在执行程序上调用shutdown()
应该足够了......尽管javadocs没有讨论shutdown()
和awaitTermination()
的内存一致性效果。
(通过阅读ThreadPoolExecutor
的{{3}},awaitTermination()
方法在池线程之间提供发生在之前(在完成所有任务之后)并且该方法的返回。发生 - 之前是由于使用执行程序的主锁来同步池关闭。很难看出它们如何在没有这个(或等效的)的情况下正确实现关闭,所以它不仅仅是一个“实施人工制品”...... IMO。)
答案 1 :(得分:0)
好吧,访问矩阵中的元素是一个非常快速的操作,而计算则不是。
我认为在计算可以并发时同步访问矩阵对你来说是一种正确的方法。
int[][] mat = new int[100][100];
Object lock = new Object();
ExecutorService threadPool = Executors.newFixedThreadPool(8);
for (int i = 0; i < 100; i++) {
Set<Integer> someText = new HashSet<>(reader.next());
int lineIndex = i;
threadPool.submit(() -> {
int result = someOperationOnText(someText);
synchronized (lock) {
mat[lineIndex] = result;
}
});
}