如何为这个CPU密集型Java应用程序提供GUI?

时间:2009-11-08 05:55:07

标签: java user-interface multithreading loops

我在Java中编写了一些遗传算法作为学校作业。到目前为止,我几乎坚持做控制台应用程序。但是我认为UI对这个程序真的很有帮助,所以我想制作一个。我无法弄清楚如何协调事件驱动的GUI和具有开始和结束的控制台应用程序。

理想情况下,我想要一组用于设置的文本框,然后是一个“开始”按钮。一旦你点击开始,算法将开始运行,GUI将在设定的时间间隔内更新最新的程序状态。如果没有算法冻结GUI,反之亦然,我怎么做到这一点?我不希望任何一个人在等另一个。

如何在算法运行时让主循环不冻结GUI?我假设他们需要在不同的线程中,但我以前从未搞过线程。这对于这项任务来说似乎过于复杂,这一点必须司空见惯。

5 个答案:

答案 0 :(得分:7)

你正在使用线程。 GUI编程要求线程,在大多数情况下 - 幸运的是,Java的线程API并不太可怕(Python的模型就是它,所以它正在做某些东西)。

不要被线程吓倒 - 虽然它是中级的,但我会说,但这是每个程序员都应该理解的东西。

那里有很多信息可能使你容易受到攻击。然而,GUI应用程序是一个非常有用的领域。线程的反对者会让你相信事件编程模型会在这种情况下帮助你,当真的,它不会。大多数人说“线程糟糕”提出的解决方案往往比自己更糟糕。

您可以尝试将您的解决方案整合到一个线程中,但这需要您的CPU密集型代码以可预测的时间间隔向GUI提供。那解决方案很糟糕。 编辑:由于其他人建议这种方法,让我详细说明为什么它很糟糕:你不知道,有些东西总是在GUI中更新。当您将窗口移到顶部然后关闭时,该窗口下的整个区域都将失效,并且代码必须执行 - 在您的过程中 - 重绘该部分。即使您非常快速地更新GUI,这也会提供负面的用户体验,因为简单的GUI操作完全阻止。有时候鼠标悬停时会突出显示按钮。用户右键单击。所有这些都需要CPU时间来实现,如果你的孤独线程正在咀嚼你的GA,它们就不会发生。正在执行的GUI代码不仅仅是您的代码。

这似乎是very useful article on the topic

关于该主题的两个课程是:

  1. Concurrency
  2. Concurrency in Swing

答案 1 :(得分:5)

抱歉 - 看起来后台任务看起来很简单明了。不幸的是,Java Swing GUI线程模型有点复杂。在这方面已经有了一些改进,但是仍然需要先了解一些线程知识。

如果你有时间,我建议阅读Filthy Rich Clients中的线程章节 - 通过SwingWorker进行无痛线程化。

alt text

如果您不耐烦,只需阅读SwingWorker上的JavaDoc即可。如果您真的不耐烦,只需从JavaDoc示例用法中复制生命含义示例。

答案 2 :(得分:3)

当我在大学为我的一个计算机图形课程编写光线跟踪器时,我有一个长期运行的任务,我想在跟踪器绘图时定期更新显示器。我使用了两个独立的线程 - 一个线程休眠和更新(比如每500毫秒);另一个线程执行实际的光线跟踪。关键是要在一个公共对象上进行同步 - 在我的例子中,访问我的图像缓冲区是同步点(一个线程无法在没有先等待直到另一个线程完成读取的情况下对图像缓冲区进行更改)。

对于您的GA处理,您可能会有类似这样的内容(伪代码):

假设您有一些对象generationHistoryObject,它存储了您希望在GUI中显示的状态,那么:

(在线程#1中)

Generation newGeneration = doMutationAndTestThisGeneration(lastGeneration);
synchronized (generationHistoryObject) {
 generationHistoryObject.updateWithNextGeneration(newGeneration);
}

(在线程#2中)

while (!programIsDone()) {
 synchronized (generationHistoryObject) {
  drawGuiForCurrentState(generationHistoryObject);
 }
 Thread.sleep(500);
}

这个想法是你为每一代孤立地完成耗时的工作,然后更新GUI必须在synchronized块中访问的部分(使GUI等待绘制直到更新完成)。

答案 3 :(得分:2)

Swing的问题在于它是单线程的(这是一件好事)所以你想让你的工作摆脱Swing线程,这样你的应用程序就能保持响应。

您需要做的是将核心算法转换为Runnable,因为它可以由SwingWorker和较新的Executors轻松处理(请参阅许多预配置的执行程序)。您还可以创建调查如何将PrintStream创建到JTextPanel,以便您可以使用标准println语句输出当前状态信息。

如果要添加停止按钮,则需要了解线程模型,以便了解如何控制它。 Java Tutorial在这方面有很好的材料以及Swing编程。强烈推荐。

http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html

答案 4 :(得分:1)

由于您的应用程序与遗传算法有关,您可以每一代左右更新GUI。您可以通过在算法代码中实现next()方法并从GUI调用它来实现此目的。这应该很简单。

但是,如果您真的不希望GUI在等待算法时冻结,那么您应该选择线程。