我正在学习 java 并发编程,并为 Game of Life 编写模拟。
这是我的想法:
- 使用 int[][] 来存储细胞的状态
- 将 int[][] 分成 t 个段并使用 t 个工作线程
- 这 t 个线程将从它们的段中读取,计算它们段中所有单元格的新值并更新单元格。
- 一旦他们完成计算,他们就会在障碍处等待其他 worker 完成
- 当越过障碍时,主线程将更新 UI。
- 工作人员继续计算下一个状态。
现在将在段的公共(public)边界处发生争用。如果一个线程在其邻居读取之前的值之前覆盖了边界单元的状态,则邻居的计算将是错误的。
我有哪些选择?
- 使用 callable 而不是 runnable 并让工作线程返回新值(而不是更新段本身)。主线程可以在越过障碍后更新矩阵。此选项涉及将工作线程返回的结果复制到矩阵中。
- 使用两个障碍。工作线程从邻居的段中复制边界单元格,并在第一个屏障处等待。一旦通过这个障碍,他们就会继续计算下一个状态并更新适当的段。然后他们在第二个障碍处等待。主线程更新 UI。
我的问题是,有没有其他方法可以不涉及复制数据或比上述两种方法更有效来处理边界单元格的争用?可能正在使用 ReaderWriterLock、volatile 变量或其他同步机制?
更新:到目前为止 double buffering solution by Peter是最干净的。但我有一个问题。 由于这两个数组是共享数据并且我们没有使用任何同步(同步访问或 volatile 变量),是否会产生可见性问题?多个 CPU 是否可以缓存数组值并在每次迭代时仅更新数组的一部分?然后线程将获得边界单元格的陈旧值。这可能吗?如果不是,为什么。如果是,我该如何解决?好像declaring two arrays volatile will not make their individual elements volatile .
最佳答案
我建议使用 2 个 int[][] 数组。我们称它们为 A 和 B。A 将保存所有奇数“刻度”的值,B 将保存偶数刻度。
将 A 初始化为您的初始状态。然后让你的线程松散计算每个单元格的下一个状态,并将结果放在 B 中相应的单元格中。 一旦所有线程都完成,B 中就有了新状态。现在,使用 B 计算每个单元格的下一个状态,并将结果存储在 A 中。在任何给定时间,一个数组将是只读的,另一个数组将是只读的只写,所以永远不会有任何争用。
优点:
- 与您现在所做的相比,不会复制数据。每个单元只发生一次写入。
- 不必担心边缘/角落情况,因为算法很简单。
- 没有正在进行的内存分配。只需在开始时分配这两个数组即可。
缺点:
- 您需要分配两倍的内存。
关于java - Conway 生命游戏的多线程 Java 程序 - 边界单元的竞争,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2328985/