2017-03-16 82 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

相关问题