我遇到了SEEM会陷入僵局的事情。死锁听起来有点像:
AWT-EventQueue-0
和frameThread
。
这是使用我构建的自定义库,但尚未完全开发(您可能称之为alpha-beta阶段?)。我决定用它做一个Pong游戏。实际上我的导师给了我这个游戏。我只是想用它来使用我的库。
我的库使用Swing组件,我怀疑它与它有什么关系。
我想指出intrinsic locks according to the oracle tutorials状态
“当线程调用synchronized方法时,它会自动获取该方法对象的内部锁定,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会发生锁定释放。”
在做这个说法之前,我已经完成了一个synchronized块,从我在程序中知道的唯一一个可以锁定的线程获取锁。失败。所以我使方法同步,并且,上面列出的要点发生了。
我的代码是
// Threads
static ThreadManager tm = new ThreadManager() {
@Override
protected void runFrameThread() {//ThreadManager has threads in it that you can start.
while (true) { //These are just the abstract inherited methods the
Main.jpane.repaint(); // threads inside the manager call
}
}
@Override
protected void runMathThread() {
}
@Override
protected void runIntenseMathThread() {
}
};
// set frame rate
static {
tm.setFPS(30L);
}
public synchronized void draw(Graphics g) {// main problem: synchronized method here.
try {
wait(hertz);
} catch (InterruptedException e) {
System.err.println("ERROR: " + e.getLocalizedMessage());
e.printStackTrace();
}
g.setColor(rgb);
g.fillRect(this.x, this.y, width, height);
}
如果这没有帮助,您可以尝试查看我的代码......
My Code Repository for the Pong game
我最好的选择是我做了什么来推迟这个方法的错误。我想要做的是以'x'hertz
的设定速率为每个对象提供更新率。如果它是返回类型方法(非空),它会更容易。
答案 0 :(得分:3)
你说:
我的库使用Swing组件,我怀疑它与它有什么关系
我担心你可能会错。您似乎使用while循环完全阻止了Swing事件调度线程(EDT),并且由于此线程负责所有Swing图形和用户交互,因此这将有效地冻结您的GUI。
while (true)
循环,而是使用Swing Timer。paintComponent
(即,不要在paintComponent中调用updateGame()
方法)。这是因为您永远无法完全控制是否调用此方法。它可以由JVM调用,以响应操作系统清除脏区域的请求,如果重新绘制请求正在堆叠,JVM可能会忽略您的重绘请求。答案 1 :(得分:2)
这似乎是一个死锁,它肯定与使用Swing有关。整个应用程序挂起的症状通常是由事件派发线程(EDT)死锁引起的。
问题似乎出现在此代码中,该代码位于Ball.java
类:
public synchronized void draw(Graphics g) {
try {
wait(hertz);
} catch (InterruptedException e) {
...
}
hertz
字段似乎未显式初始化,因此其默认值为0L
。 wait(timeout)
方法的值为零将无限期地阻塞,也就是说,它将等待没有超时。它似乎不会被通知此对象,因此此方法将永久挂起调用线程。此方法是从JPanel.paintComponent
方法调用的,该方法由EDT调用,因此冻结了应用程序的用户界面。
不要试图通过在绘画程序中休眠或暂停来控制更新率。绘图程序应始终尽可能少地工作,检查模型的当前状态并发出适当的图形调用,并尽快返回。
您的程序是多线程的,因此您需要同步对象。线程管理器线程正在更新游戏对象,EDT正在使用游戏对象进行绘制。
在多个线程上将访问您的对象,因此需要同步。同步块(或方法)应该尽可能少:进入,更新(或绘制),然后离开。 EDT上的代码永远不应该调用wait
,因为这可能会导致重复性能和响应能力下降。更新线程应该在锁之外执行尽可能多的计算,并且只需在保持锁定时使用新值更新游戏对象。这将最小化在重绘周期期间EDT被阻止获取锁定的时间量。