簡體   English   中英

反復啟動和停止多個線程

[英]Repeatedly start and stop several threads

我有一個網格(矩陣)。 每個單元格都是一個對象。 最初,這些單元中有五個(隨機)被選為“跳躍單元”,用戶可以在其中跳躍以逃避敵人。 每個單元格都有一個隨機倒計時。 如果倒數變為0,則該單元格將變為普通單元格,玩家將無法再跳入該單元格。 如果用戶按Enter鍵,則玩家會跳入當前的“跳格”之一,同時會隨機選擇一個新的跳格。

有時,當我在玩這個游戲並按Enter鍵時,會收到ConcurrentModificationException ,我不知道為什么。

可以按Esc鍵以重置網格狀況(創建新的5個跳轉單元)。 如果我反復按Esc鍵,則經常會發生一些新的跳轉單元不會減少其倒計時的情況。

這是代碼:

細胞

public class Cell implements Runnable {

/** indicates whether this cell is the target cell */
private boolean isTarget;

/** indicates whether this cell is a jump cell */
private boolean isJumpCell;

/** associated number to this cell */
private int number;

/** living time of this cell */
private int countdown;

/** coordinates of this cell in the grid */
private int i;
private int j;

private boolean running;
private boolean alreadyStarted;

private Thread countdownT;

public Cell(int i, int j) {
    this.i = i;
    this.j = j;
    countdown = (int)(Math.random() * 6) + 3;
    countdownT = new Thread(this);
}

/**
 * Starts the thread or restart if already started in the past
 */
public void start() {
    if(alreadyStarted) {
        restart();
    } else {
        countdownT.start();
        running = true;
        alreadyStarted = true;
        isJumpCell = true;
    }
}

/**
 * This cell is restarted
 */
public void restart() {
    isJumpCell = true;
    running = true;
    countdown = (int)(Math.random() * 6) + 3;
}

/**
 * Stops the update of this cell (is not a jump cell anymore)
 */
public void stop() {
    isJumpCell = false;
    running = false;
}

@Override
public void run() {
    while (running) {
        try {
            Thread.sleep(1000);
            countdown--;
            // if the countdown becomes 0, stop running
            // not a jump cell anymore
            if (countdown == 0) {
                running = false;
                isJumpCell = false;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

格網

我把基本代碼

public Grid(RedEnemy p) {
    this.p = p;
    grid = new Cell[ROWS][COLS];
    for(int i = 0; i < ROWS; i++) {
        for(int j = 0; j < COLS; j++) {
            grid[i][j] = new Cell(i,j);
        }
    }
    jumpCells = new ArrayList<Cell>();

    // initial setup
    numJumpCells = 5;

    grid[target_y][target_x].setTarget(true);

    for(int n = 0; n < numJumpCells; n++) {
        int i = (int) (Math.random() * (ROWS - 1)); 
        int j = (int) (Math.random() * (COLS - 1)); 
        grid[i][j].setTarget(false);
        grid[i][j].setNumber(n + 1);
        jumpCells.add(grid[i][j]);
        jumpCells.get(n).start();
    }
}

public void reset() {
    for(Cell cell : jumpCells) {
        cell.stop();
    }
    jumpCells.clear();
    target_x = 0;
    target_y = 0;
    numJumpCells = 5;
    for(int n = 0; n < numJumpCells; n++) {
        int i = (int) (Math.random() * (ROWS - 1)); 
        int j = (int) (Math.random() * (COLS - 1)); 
        grid[i][j].setTarget(false);
        grid[i][j].setNumber(n + 1);
        jumpCells.add(grid[i][j]);
        jumpCells.get(n).start();
    }
}

/**
 * Performs the jump
 */
public void jump() {
    // always jumps in the first jump cell among the current ones
    Cell jumpCell = jumpCells.get(0); 
    // updates the position of the player
    target_y = jumpCell.getI();
    target_x = jumpCell.getJ();
    // updates the cell in the grid
    grid[jumpCell.getI()][jumpCell.getJ()].setJumpCell(false);
    grid[jumpCell.getI()][jumpCell.getJ()].setTarget(true);
    // stop the cell in which the player is jumped
    jumpCells.get(0).stop();
    // removes the cell from the list of the jump cells
    jumpCells.remove(0);

    // updates the position of the player in the enemy class
    p.setTargetY(target_y * SIZE);
    p.setTargetX(target_x * SIZE);

    // create a new jump cell at random
    int i = (int) (Math.random() * (ROWS - 1)); 
    int j = (int) (Math.random() * (COLS - 1)); 
    //grid[i][j].setJumpCell(true);
    grid[i][j].setTarget(false);
    grid[i][j].setNumber(jumpCells.size() - 1);
    jumpCells.add(grid[i][j]);
    jumpCells.get(jumpCells.size() - 1).start();
}

// UPDATE
public void update(float delta) {
    for(Iterator<Cell> it = jumpCells.iterator(); it.hasNext();) {
        Cell c = it.next();
        if(!c.isJumpCell()) {
            it.remove();
            numJumpCells--;
        }
    }
}

// DRAW
public void draw(Graphics dbg) {
    // draw the grid
    dbg.setColor(Color.BLACK);
    int heightOfRow = GamePanel.PHEIGHT / ROWS;
    for (int i = 0; i < ROWS; i++)
        dbg.drawLine(0, i * heightOfRow, GamePanel.PWIDTH, i * heightOfRow);

    int widthdOfRow = GamePanel.PWIDTH / COLS;
    for (int i = 0; i < COLS; i++)
        dbg.drawLine(i * widthdOfRow, 0, i * widthdOfRow, GamePanel.PHEIGHT);

    // draw jump cells
    dbg.setColor(Color.RED);
    dbg.setFont(new Font("TimesRoman", Font.PLAIN, 25)); 
    for(Iterator<Cell> it = jumpCells.iterator(); it.hasNext();) {
        Cell c = it.next();
        dbg.drawRect(c.getJ() * SIZE, c.getI() * SIZE, SIZE, SIZE);
        dbg.setColor(Color.BLUE);
        dbg.drawString(String.valueOf(c.getCountdown()), c.getJ() * SIZE + SIZE/2, c.getI() * SIZE + SIZE/2);
        dbg.setColor(Color.RED);
    }

    // draw target
    dbg.setColor(Color.BLUE);
    dbg.fillRect(target_x * SIZE, target_y * SIZE, SIZE, SIZE);
}

編輯

計時器線程

public class TimerThread implements Runnable {

private Grid grid;

private boolean isRunning;

public TimerThread(Grid grid) {
    this.grid = grid;
}

public void start() {
    isRunning = true;
}

public void stop() {
    isRunning = false;
}

@Override
public void run() {
    while(isRunning) {
        // retrieves the list of the jump cells
        ArrayList<Cell> jumpCells = (ArrayList<Cell>) grid.getJumpCells();

        // wait one second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // updates the jump cells
        for(Cell c : jumpCells) {
            if(c.getCountdown() > 0) {
                c.decreaseCountdown();
            } else {
                c.setJumpCell(false);
            }
        }
    }
}

要么...

@Override
public void run() {
    while(isRunning) {
        // retrieves all the cells
        Cell[][] cells = grid.getGrid();

        // wait one second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // updates the cells
        for(int i = 0; i < cells.length; i++) {
            for(int j = 0; j < cells[0].length; j++) {
                if(cells[i][j].isJumpCell()) {
                    if(cells[i][j].getCountdown() > 0) {
                        cells[i][j].decreaseCountdown();
                    } else {
                        cells[i][j].setJumpCell(false);
                    }
                }
            }
        }
    }
}

好的,最后一次更新似乎正常! :)

這只是一個猜測,因為您沒有要跟蹤的堆棧跟蹤信息,但是我懷疑當您收到ConcurrentModificationException正在發生的事情是您在執行更新的同時(通過jumpCells迭代)嘗試計算跳轉的效果(並刪除其中一個jumpCells )。 那是不允許的。

您可以使用並發集合來允許類似這樣的操作,但這只會強制阻塞,並且對於您要執行的操作不是很干凈。

您僅應在更新期間更改跳轉單元。 您可以通過設置jump方法設置一些在更新過程中進行檢查的變量來實現。

最好的辦法是不要對單元格計時器使用多線程。 沒有理由在此應用程序中使用這么多線程。

而是使用單個計時器線程,該線程每秒(甚至每100毫秒)遍歷所有單元並更新它們。 向單元格添加同步鎖,以確保它們不會被游戲循環和計時器同時修改。

完整的堆棧跟蹤信息將有助於診斷您遇到的確切問題。 而且,如果您必須使用多線程,請學習如何很好地使用調試器。

產生異常的原因是,當draw方法迭代列表時,您有多個線程可能會更新jumpCells列表。 這同樣適用於update方法...,它也正在更新列表(通過it.remove )。 如果更新和迭代在時間上重疊,則將獲得ConcurrentModificationException

(在標准非並行列表類中有一個“快速失敗”檢查功能,旨在檢測您同時迭代和更新列表的情況。它旨在消除更隱蔽且更難發現的問題;例如,損壞列出數據結構。)

您在單元上的操作還存在潛在的並發問題……除非正確地同步獲取程序和設置程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM