1

我已经完成了一个 GUI 应用程序。我有四个班级:Main, UserWindow, Task1, Task2. Main类包含一个布尔变量buttonStartPressedMain方法启动UserWindow该类的一个实例,并一直等到用户按下“开始”按钮。当用户按下开始按钮(在 中UserWindow)时,ActionListener分配truestatic boolean buttonStartPressed并且Main方法继续。

主.java

public static void ......
static boolean buttonStartPressed = false;

...........

while (!buttonStartPress) {
Thread.sleep(50);
}

Task1 t1 = new Task1();

.....
}
}

它工作正常,但我不喜欢while循环。我觉得这不是编写应用程序的常规方式。还有另一种方法:我可以组合MainUserWindow类,ActionListener(buttonPressed)的结果将是Task1的开始。但是,另一方面,我认为Mainclass 和UserWindowclasses 应该彼此分开。

4

2 回答 2

3

是的,那是错误的。你不应该有繁忙的循环......任何地方。

你甚至需要 buttonStartPressed 变量吗?为什么您有兴趣知道它是否已被按下,当按下按钮时执行某些操作不是主要思想吗?

您应该在您的actionPerformed()方法中创建 Task1,并根据您要执行的操作,启动一个将执行任务的线程(或者如果它真的很快就在 EDT 中运行它,所以它不会冻结图形用户界面)。

于 2013-10-03T18:10:22.290 回答
2

Swing(以及几乎每个 GUI 工具包)都有一个专用线程。这个线程,即事件调度线程,在您第一次需要时启动。这通常是当你setVisible一个JFrame. 这个线程是一个巨大的循环,它的作用是消耗输入事件和重绘事件并相应地运行一些逻辑。

在您的情况下,您实际上有两个线程。第一个是main线程,第二个是EDT. 您的main线程正在忙等待。您的代码在用户按下按钮后立即actionListener执行。EDT

您正在使用布尔变量作为使两个线程进行通信的一种方式。使用一些共享内存确实是进行线程间通信的一种可能方式。

现在,正如您所怀疑的那样,您应该避免进行繁忙的等待。它无谓地消耗CPU时间,每次唤醒都会干扰其他线程,并且不可避免地会有反应延迟。

使用共享内存进行通信通常也很糟糕。这是一种太低级的交流方式,而且经常做错。从两个线程访问一条数据必须受到锁定机制的保护。即使是像布尔值这样简单的数据也会咬你,因为不能保证如果一个线程写入它,另一个线程会看到修改。在您的示例中,至少应声明布尔值volatile具有此保证。

因此,添加volatile关键字后,您的解决方案就起作用了:您有一个EDT正在愉快地做事的人,当用户单击按钮时,main线程就会执行Task1。首先要问自己的问题是:是Task1一项耗时的任务吗?实际上,最简单的解决方案是在 中运行 Task1,方法是EDTactionListener. 请注意,在EDT冻结 GUI 中执行某些代码。如果Task1持续时间少于 100 毫秒,用户甚至不会注意到冻结,并且在另一个线程中执行它是没有意义的。如果您担心将 GUI 类与“任务”类耦合,那么您应该只使用观察者模式来防止直接依赖。

如果任务很耗时并且您不希望 GUI 冻结,那么您应该使用多个线程。一种解决方案是您实施的解决方案。它有点有限,因为你只有一个main线程,但它可以工作。您现在的问题是使这些线程进行通信。线程间通信中一个非常常见的模式是使用阻塞队列。这也是一个共享的数据,但它被设计为由多个线程使用。一个线程 ( EDT) 写入它 ( add()),另一个从它读取 (take()) 并阻塞,直到某些内容被写入。对于您的简单示例,这似乎有点过头了,但它是在线程之间共享数据的一种非常方便的方式。写入阻塞队列的对象可以是任何东西;例如,它们可以表示要执行的命令。

从 GUI 执行耗时功能的更传统方法是在需要时创建或使用专用线程。这可以使用低级 API ( Thread ) 或使用高级 API ( ExecutorService ) 来完成,两者都非常易于使用。同样,如果您想将 GUI 操作与线程创建分离,请使用观察者模式。

如果这堵文字墙没有为您的问题提供简单的答案,我深表歉意,但是当我们混合 GUI 和线程时,有很多事情需要考虑。我希望它对您有用,以了解您的其他选择是什么。

于 2013-10-03T23:28:26.907 回答