【问题标题】:Getting out of a potential deadlock摆脱潜在的僵局
【发布时间】:2014-07-13 03:46:55
【问题描述】:

我遇到了似乎是僵局的情况。死锁听起来有点像:

  • 无法关闭窗口
  • 如果没有 IDE 上的终止按钮,则无法终止
  • 空白,没有任何反应,完全没有异常或错误。

如果这些都是在死锁中发生的事情,那么我可能已经解决了一半的问题。我知道有两个线程正在运行:AWT-EventQueue-0frameThread

这是使用我构建但尚未完全开发的自定义库(您可能称之为 alpha-beta 阶段?)。我决定用它来制作乒乓球游戏。其实我的导师给我安排了这个游戏。我只是打算使用我的图书馆。

我的库使用 Swing 组件,我怀疑这与它有什么关系。

我想指出,intrinsic locks according to the oracle tutorials 声明

“当线程调用同步方法时,它会自动获取该方法对象的内在锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会释放锁。”

在执行此操作之前,我已经完成了一个同步块以从我所知道的程序中唯一可以拥有锁的线程获取锁。失败的。所以我使方法同步,并且上面列出的要点发生了。

我的代码是

// 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 的设定速率为每个对象设置更新速率。如果它是一个返回类型的方法(不是 void)会更容易

【问题讨论】:

    标签: java multithreading swing


    【解决方案1】:

    你说:

    我的库使用 Swing 组件,我怀疑这与它有什么关系

    我担心你可能大错特错。您的 while 循环似乎完全阻塞了 Swing 事件调度线程(EDT),并且由于该线程负责所有 Swing 图形和用户交互,这将有效地冻结您的 GUI。

    • 不要使用会阻塞 EDT 的 while (true) 循环,而是使用 Swing Timer。
    • 不要暂停您的图形绘制,因为这会使您的程序看起来反应迟钝。
    • 不要从绘画方法中调用同步。
    • 不要在诸如paintComponent 之类的绘制方法中更改对象的状态(即,不要从paintComponent 中调用updateGame() 方法)。这是因为您永远无法完全控制是否或是否会调用此方法。 JVM 可以在响应操作系统清除脏区的请求时将其调用到您无法控制的范围内,如果重绘请求堆积如山,JVM 可能会忽略您的重绘请求。

    【讨论】:

    • 感谢您对如何做的非常直截了当的解释。我不知道。当时我还认为 Swing 与它无关,但后来我记得 Swing 不喜欢 Oracle java 文档中的并发。
    【解决方案2】:

    这看起来确实是一个死锁,而且它肯定与 Swing 的使用有关。整个应用程序挂起的症状通常是由事件调度线程(EDT)被死锁引起的。

    问题似乎出在这段代码中,在您的Ball.java 类中:

    public synchronized void draw(Graphics g) {
        try {
            wait(hertz);
        } catch (InterruptedException e) {
        ...
    }
    

    hertz 字段似乎没有显式初始化,因此它的默认值为0Lwait(timeout) 方法的值为零将无限期地阻塞,也就是说,它将等待而没有超时。似乎从未通知过此对象,因此此方法将永远挂起调用线程。此方法是从 EDT 调用的 JPanel.paintComponent 方法调用的,因此这会冻结应用程序的用户界面。

    不要试图通过在绘画例程中休眠或暂停来控制更新速率。绘制例程应始终尽可能少地完成工作,检查模型的当前状态并发出适当的图形调用,并尽快返回。

    您的程序是多线程的,因此您需要同步您的对象。线程管理器线程正在更新游戏对象,而 EDT 正在使用游戏对象进行绘画。 对对象的访问将发生在多个线程上,因此需要同步。同步块(或方法)应该尽可能少地做:进入、更新(或绘制)和退出。 EDT 上的代码永远不应调用wait,因为这可能会导致糟糕的重绘性能和响应能力。更新线程应该在锁之外进行尽可能多的计算,并在持有锁时简单地用新值更新游戏对象。这将最大限度地减少 EDT 在重绘周期期间被阻止获取锁的时间。

    【讨论】:

    • 您的贡献有助于更深入地解释而不是快速概述。另外,我很新,这是我的第一场比赛。只有 2 年的经验,所以我可以使用我能得到的所有帮助。谢谢!
    • 哦,赫兹是在创建 Paddle 对象和 Ball 对象时初始化的。 (他们做同样的事情)。除了在一个新问题中,我可能不应该问这个问题,但是我应该如何为某些对象提供不同的更新率?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    相关资源
    最近更新 更多