我有一个代表生命游戏板的二维数组。N
线程在数组的自己的部分工作,创建下一代。
当所有N
的锁都用完后,我打印当前一代并在进入下一代之前休眠一段时间。它是通过使用来实现的CyclicBarrier
。
尽管该程序有效,但我认为打印线程可能会看到陈旧的数组单元,因为对数组的写入不同步。内部CyclicBarrier
使用锁,所以它可能会触发happens-before
,但我不确定。
我看到的唯一解决方案是锁定Board
方法。但我不想使用锁,因为每个线程都有自己专用的数组部分,它会在锁上产生不必要的争用。
public class GameOfLife {
private final Worker[] workers;
private final long duration;
private final AtomicBoolean done;
private Board current;
public GameOfLife(Board initialBoard, long duration) throws InterruptedException {
initialBoard.print();
this.current = new Board(initialBoard);
this.duration = duration;
this.done = new AtomicBoolean(false);
int cores = Runtime.getRuntime().availableProcessors();
this.workers = new Worker[cores];
CyclicBarrier barrier = new CyclicBarrier(cores, () -> {
current.print();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
GameOfLife.this.done.set(true);
return;
}
Board oldBoard = current;
current = new Board(current);
for (Worker worker : this.workers) {
worker.setOldBoard(oldBoard);
worker.setNewBoard(current);
}
});
int partitionSize = (initialBoard.getRows() / cores) + 1;
for (int core = 0; core < cores; core++) {
workers[core] = new Worker(barrier, initialBoard, current, done, core * partitionSize, Math.min(initialBoard.getRows(), (core + 1) * partitionSize));
}
}
public void start() {
for (Worker worker : this.workers) {
new Thread(worker).start();
}
new Timer(true).schedule(new TimerTask() {
@Override
public void run() {
GameOfLife.this.done.set(true);
}
}, this.duration);
}
public static void main(String[] args) throws InterruptedException {
boolean[][] initialBoard = new boolean[27][27];
for (int row = 10; row < 20; row++) {
initialBoard[row][15] = true;
}
GameOfLife e = new GameOfLife(new Board(initialBoard), 40000);
e.start();
}
}
class Worker implements Runnable {
private final CyclicBarrier barrier;
private final AtomicBoolean done;
private volatile Board oldBoard;
private volatile Board newBoard;
private final int rowStart;
private final int rowEnd;
public Worker(CyclicBarrier barrier, Board oldBoard, Board newBoard, AtomicBoolean done, int rowStart, int rowEnd) {
this.oldBoard = oldBoard;
this.newBoard = newBoard;
this.done = done;
this.rowStart = rowStart;
this.rowEnd = rowEnd;
this.barrier = barrier;
}
@Override
public void run() {
while (!done.get()) {
for (int row = rowStart; row < rowEnd; row++) {
for (int col = 0; col < this.oldBoard.getCols(); col++) {
int neighbors = this.oldBoard.countNeighbors(row, col);
if (this.oldBoard.isAlive(row, col)) {
if (neighbors < 2 || neighbors > 3) {
this.newBoard.kill(row, col);
}
} else {
if (neighbors == 3) {
this.newBoard.create(row, col);
}
}
}
}
try {
barrier.await();
} catch (Exception e) {
System.out.println(e);
return;
}
}
}
public void setOldBoard(Board oldBoard) {
this.oldBoard = oldBoard;
}
public void setNewBoard(Board newBoard) {
this.newBoard = newBoard;
}
}
class Board {
private final boolean[][] board;
Board(Board another) {
this(another.board);
}
Board(boolean[][] aBoard) {
this.board = new boolean[aBoard.length][aBoard[0].length];
for (int row = 0; row < aBoard.length; row++) {
this.board[row] = Arrays.copyOf(aBoard[row], aBoard[row].length);
}
}
public int countNeighbors(int row, int col) {
int counter = 0;
int[][] possibleNeighbors = {{row - 1, col - 1}, {row, col - 1}, {row + 1, col - 1}, {row - 1, col},
{row + 1, col}, {row - 1, col + 1}, {row, col + 1}, {row + 1, col + 1}};
for (int[] possibleNeighbor : possibleNeighbors) {
final int r = possibleNeighbor[0];
final int c = possibleNeighbor[1];
if (insideBoard(r, c) && this.board[r][c]) {
counter++;
}
}
return counter;
}
private boolean insideBoard(int row, int col) {
return row >= 0 && row < this.board.length && col >= 0 && col < this.board[row].length;
}
public int getRows() {
return this.board.length;
}
public int getCols() {
return this.board[0].length;
}
public boolean isAlive(int row, int col) {
return this.board[row][col];
}
public void kill(int row, int col) {
this.board[row][col] = false;
}
public void create(int row, int col) {
this.board[row][col] = true;
}
public void print() {
for (int row = 0; row < this.board.length; row++) {
for (int col = 0; col < this.board[row].length; col++) {
System.out.print(this.board[row][col] ? "*" : " ");
}
System.out.println();
}
}
}