2017-03-16 83 views
0

因此,我寫了一個小程序,它將移動圓周,當它們碰撞時它們會向相反的方向移動,但是當我試圖延遲執行時,它們不會快速移動,我得到java.lang.IllegalMonitorStateException 鎖定在canvasRender.java,創建一個實例:更改所有者爲ReentrantLock

ReentrantLock renderLock = new ReentrantLock(); 

方法,將暫停執行了一會兒,所以圈子不能走動超級快。

publlic void delay(){  
     renderLock.unlock(); 
     try { Thread.sleep(10); } catch (Exception e) {} ; 
     renderLock.lock(); 
    } 

然後從那裏我創建一個窗口,並添加的ActionListener另一個類

public static void main(String[] args){ 
//Buttons and other elements 
// ... 
JButton start = new JButton("Start!"); 
createAndShowGUI(); 
} 

在createAndShowGUI():

static void createAndShowGUI(){ 
//adding elements to panels  
start.addActionListener(new ActionListener() { 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     start(); //this will set gameIsRunning variable to true and create models 
     while (gameIsRunning) { 
      //update(); //which has delay(); at the end of frame drawing 
      //but even if just put delay() 
      delay(); //still says exception 
      start.setEnabled(false); //while game is running button is unavailable 
      } 
      start.setEnabled(true); 
    } 
}); 
} 

在這種情況下,我的鎖由Thread main擁有,但當我點擊按鈕'開始!'時當前是Thread AWT-EventQueue-0,所以程序崩潰。如何解決這個問題? (或我在哪裏愚蠢?)

回答

0

問題是你在renderLock.lock()main調用後,從AWT-EventQueue-0呼叫renderLock.unlock()。線程AWT-EventQueue-0不允許調用unlock()它,因爲它不是首先調用lock()它的線程。

你或許可以通過刪除ReentrantLock並僅使用​​來簡化操作。


我不知道你的程序的其餘部分的設計,但在我看來,該while循環的內容在一個單獨的線程屬於。您通常不想循環使用UI偵聽器方法(如actionPerformed()ActionListener),因爲它會凍結GUI。

有一兩件事你可以做的是增加一個Object同步上:

private static final Object LOCK = new Object() 

隨後的比賽,更新邏輯移動到它自己的線程 - 是這樣的:

private static class GameThread extends Thread { 
    public GameThread() { 
     super("GameThread"); 
    } 

    public void run() { 
     synchronized (LOCK) { 
      start(); 
      while (gameIsRunning) { 
       update(); 
       try { 
        // Try to sleep for 10 millis: 
        LOCK.wait(10); 
       } catch (InterruptedException ignored) { } 
      } 
     } 
     // Re-enable the button: 
     javax.swing.SwingUtilities.invokeLater(() -> start.setEnabled(true)); 
    } 
} 

,你可以將您的ActionListener更改爲僅禁用該按鈕並啓動GameThread

start.addActionListener(new ActionListener() { 
    @Override 
    public void actionPerformed(ActionEvent e) { 
     start.setEnabled(false); 
     synchronized(LOCK) { 
      if(!gameIsRunning) { 
       new GameThread().start(); 
      } 
     } 
    } 
}); 

任何其他檢查或修改遊戲狀態的代碼也應包含在synchronized (LOCK)塊中。如果update()修改了GUI以及遊戲狀態,那麼它可能需要使用SwingUtilities.invokeLater()來完成。

也可以更清楚地將start()重命名爲setupGame()JButton startJButton startButton

+0

聖,這是很多的幫助!當屏幕上繪製大量對象時,我使用ReentrantLock減少閃爍。如果我使用同步而不是鎖定,你認爲問題會持續嗎? –

+0

我不是專家,尤其是使用圖形用戶界面,但同步/鎖定是關於控制對共享資源的訪問 - 可能與您的案例中的閃爍無關。我會看看[這個答案](https://stackoverflow.com/a/17966278/1232459)。也許你應該開始不經常地更新UI。如果您將GUI更新與遊戲狀態更新分開,可能會更容易管理 - 例如在run()的頂部聲明'long lastUIUpdateTime = 0',並且只觸發UI更新if(System.currentTimeMillis() - lastUIUpdateTime> 40)'。 (當然,當你這樣做的時候更新'lastUIUpdateTime'。) – rjb1290

相關問題