我正在学习 Java 中的并发编程,并为 Game of Life 编写模拟。
这是我的想法:
- 使用 int[][] 存储单元格的状态
- 将 int[][] 划分为 t 个段并使用 t 个工作线程
- t 线程将从它们的段中读取,计算其段中所有单元的新值并更新单元。
- 一旦他们完成计算,他们就会在障碍处等待其他工人完成
- 当越过障碍时,主线程将更新 UI。
- 工人继续计算下一个状态。
现在将在段的共同边界处发生争用。如果一个线程在其邻居读取之前的值之前覆盖了边界单元的状态,则邻居的计算将是错误的。
我有哪些选择?
- 使用 callable 而不是 runnable 并让工作线程返回新值(而不是更新段本身)。主线程可以在越界后更新矩阵。此选项涉及将工作线程返回的结果复制到矩阵中。
- 使用两个屏障。工作线程从其邻居的段复制边界单元并在第一个屏障处等待。一旦通过了这个障碍,他们就会继续计算下一个状态并更新适当的段。然后他们在第二道屏障等待。主线程更新 UI。
我的问题是,有没有其他方法可以处理不涉及复制数据或比上述两个选项更有效的边界单元格的争用?可能是在使用 ReaderWriterLock、volatile 变量或其他同步机制?
更新:到目前为止,彼得的双缓冲解决方案是最干净的。但我有一个问题。由于这两个数组是共享数据并且我们没有使用任何同步(同步访问或 volatile 变量),它不会造成可见性问题吗?多个 CPU 是否可以缓存数组值并在每次迭代时只更新数组的一部分?然后线程将获得边界单元格的陈旧值。这可能吗?如果不是,为什么。如果是,我该如何解决?似乎声明两个数组 volatile 不会使它们的各个元素 volatile。