Swing(以及几乎每个 GUI 工具包)都有一个专用线程。这个线程,即事件调度线程,在您第一次需要时启动。这通常是当你setVisible
一个JFrame
. 这个线程是一个巨大的循环,它的作用是消耗输入事件和重绘事件并相应地运行一些逻辑。
在您的情况下,您实际上有两个线程。第一个是main
线程,第二个是EDT
. 您的main
线程正在忙等待。您的代码在用户按下按钮后立即actionListener
执行。EDT
您正在使用布尔变量作为使两个线程进行通信的一种方式。使用一些共享内存确实是进行线程间通信的一种可能方式。
现在,正如您所怀疑的那样,您应该避免进行繁忙的等待。它无谓地消耗CPU时间,每次唤醒都会干扰其他线程,并且不可避免地会有反应延迟。
使用共享内存进行通信通常也很糟糕。这是一种太低级的交流方式,而且经常做错。从两个线程访问一条数据必须受到锁定机制的保护。即使是像布尔值这样简单的数据也会咬你,因为不能保证如果一个线程写入它,另一个线程会看到修改。在您的示例中,至少应声明布尔值volatile
具有此保证。
因此,添加volatile
关键字后,您的解决方案就起作用了:您有一个EDT
正在愉快地做事的人,当用户单击按钮时,main
线程就会执行Task1
。首先要问自己的问题是:是Task1
一项耗时的任务吗?实际上,最简单的解决方案是在 中运行 Task1,方法是EDT
从actionListener
. 请注意,在EDT
冻结 GUI 中执行某些代码。如果Task1
持续时间少于 100 毫秒,用户甚至不会注意到冻结,并且在另一个线程中执行它是没有意义的。如果您担心将 GUI 类与“任务”类耦合,那么您应该只使用观察者模式来防止直接依赖。
如果任务很耗时并且您不希望 GUI 冻结,那么您应该使用多个线程。一种解决方案是您实施的解决方案。它有点有限,因为你只有一个main
线程,但它可以工作。您现在的问题是使这些线程进行通信。线程间通信中一个非常常见的模式是使用阻塞队列。这也是一个共享的数据,但它被设计为由多个线程使用。一个线程 ( EDT
) 写入它 ( add()
),另一个从它读取 (take()
) 并阻塞,直到某些内容被写入。对于您的简单示例,这似乎有点过头了,但它是在线程之间共享数据的一种非常方便的方式。写入阻塞队列的对象可以是任何东西;例如,它们可以表示要执行的命令。
从 GUI 执行耗时功能的更传统方法是在需要时创建或使用专用线程。这可以使用低级 API ( Thread ) 或使用高级 API ( ExecutorService ) 来完成,两者都非常易于使用。同样,如果您想将 GUI 操作与线程创建分离,请使用观察者模式。
如果这堵文字墙没有为您的问题提供简单的答案,我深表歉意,但是当我们混合 GUI 和线程时,有很多事情需要考虑。我希望它对您有用,以了解您的其他选择是什么。