2010-06-07 16 views
8

在引入Java内存模型之后,Swing准则已更改为声明任何Swing组件都需要在EDT上实例化,以避免未发布的实例状态。是否允许在非EDT线程中加载Swing类?

我无法在任何地方找到的是类加载是否也要求在EDT上,或者我们能否在后台线程中预加载关键的Swing类? Sun/Oracle对此有任何官方声明吗?是否有已知的类可以保持非线程安全的静态状态,因此需要在EDT上加载?

澄清解决Nemi的问题:这是一个实际问题。我们的应用程序启动时间的相当一部分,是在EDT上花费类加载和字体/图像加载。其中大部分可归因于Swing和相关的图书馆。

以下是som背景:与其他许多Swing应用程序一样,我们在启动时预构造了许多表单,以便使UI更加灵敏。分析后,我们发现表单构建的实际时间相对较快 - 缓慢的是所有类和字体的加载(磁盘读取为缓慢在公司设置与按访问病毒扫描仪,监视扫描仪,审计跟踪器和天知道还有什么别的硬盘驱动器)。

我们试图在后台线程中构造相同的表单(违反了Swing的规则),然后将它们丢弃。一旦我们完成了,我们在EDT上构建相同的表单,因为所有类都被加载并且其他任何文件都在磁盘缓存中,所以速度更快。它适用于我们,我们可能会继续这样做,除非发生非常糟糕的事情。

我在问的是这是安全的做法,一个好的做法还是一个黑客?

+0

特别是,两个单班我能想到的是UIManager的和AppContext的。两个javadocs都没有说明它们是否应该是线程安全的。 除了isDisposed()方法没有正确读取状态(需要同步)之外,AppContext的外观正确同步。 UIManager对所有人都是免费的,但一旦EDT开始,我不认为其他线程会改变它。 – ddimitrov 2010-06-07 01:34:05

+0

这是一个有趣的问题。这是纯粹的学术,还是有一个真正的问题,你正在试图解决?我想不出有什么好的理由可以做到这一点。 – Nemi 2010-06-07 16:28:52

+0

@Nemi,给问题增加了更多的上下文 – ddimitrov 2010-06-07 23:09:53

回答

2

证据似乎表明它是安全的 - 但是再次如ddimitrov在评论中所述 - 由于未发布的更改导致无法找到微妙的线程错误,因为典型的机器只有少量内核,和L2/L3高速缓存是共享的。 (L1缓存是每个核心,但通常非常小)。

如果您希望保证没有因后台类加载而出现问题,那么坚持在ETD上加载类可能是最安全的。为了维护一个实时UI,创建一个自定义类加载器,该加载器还可以在加载每个类之间抽取事件。 (依赖性被重新加载,所以延迟只会在加载一个类的时间内)。假设这个类加载器与应用程序一起打包,那么它可以简单地将所有类加载推迟到它的类加载器。

或者,辅助事件队列可以跨越独立线程(例如模式对话框和spin库)运行。这意味着Swing可以在任何线程上运行,只要它运行在一个线程上,并且意味着它必须是更新一致的(或者我们到目前为止都非常幸运!)在此基础上,您可以在主EDT上加载你的课程,并启动辅助EDT来抽取UI事件,保持UI的响应 - 与模态对话框功能相同。 Spin实用程序将为您提供EDT事件,或者您可以手动产生新的EDT。

+0

谢谢,这回答我的问题。 – ddimitrov 2010-06-09 23:33:15

+0

考虑到这一点,在老式的EDT上运行Swing是安全的,因为Thread.start()是一个安全的出版物。然后为了看到我们需要的变化或者线程。加入()临时调度器或产生另一个调度器(这将看到新加载的类)并杀死旧的EDT。听起来很混乱... – ddimitrov 2010-06-09 23:50:59

+0

如果您使用旋转库,细节将得到照顾。我不认为这很像你所害怕的那么混乱 - 次要的EDT可以通过发布一个事件来终止。在加载所有类之后,执行此操作,并执行Thread.join()。 – mdma 2010-06-09 23:55:03

0

虽然你在技术上是正确的 - 但我从来没有听说过在不同的线程中渲染表单时出现问题,只要你没有对它们做任何事情,原始指导方针)。

那些曾经是太阳的指引,但孙改变他们为你所指定的 - 所以我敢肯定有人发现或创建了一个潜在的冲突,但我也确定它会很难打,因为应用程序仍然有效,几乎没有遵循这些准则。

至于你的问题,我相信你可以加载类,只要你没有实例化任何对象,并仍然在最严格的指导方针。

EDT仅用于避免线程冲突,因为Swing是单线程的(按设计)。如果你只是加载类而不是实例化任何东西,那么你并没有打开任何线程问题。

+0

让我担心的是静态。 EDT规则正在解决原子性问题,但并不关心可见性 - 在一个线程中将字段设置为值并不能保证从另一个线程可见,除非正确发布完成(例如通过同步,CAS等) 。请注意,现在,两个线程将安排在同一个CPU上的机会很高。随着多核系统的出现,这个问题将变得非常重要。 – ddimitrov 2010-06-08 04:13:44

+0

@ddimitrov事情是,直到意识到,其他任何线程才有机会访问它。现在,一定有一些机会,但只是加载类不会导致问题,因为没有代码在类中运行。我不确定你不同意什么(如果有的话)。当你真正实例化它并且永远只能使用EDT时,这并不意味着EDT必须被用来加载类似OP的问题。 – 2010-06-08 15:17:35

+0

加载类时,任何静态初始化器都在加载线程中运行,并且任何非最终静态都受安全发布规则的约束。我相信在Swing中没有太多可变的静态,但是(就像我举的例子),有一些。否则,我同意问题发生的几率很低,但是如果出现问题,就不可能诊断甚至可靠地复制。 – ddimitrov 2010-06-08 23:50:05

2

这是安全的。请参阅:http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html(单线程规则)

如果您害怕或想要一些反馈/调试,请查看此FEST/Swing工具:http://fest.easytesting.org/swing/wiki/pmwiki.php?n=FEST-Swing.EDT(“测试访问GUI组件是在EDT中完成的)” - 这是自定义RepaintManager,当您违反EDT访问策略时会失败。

你会发现这是很有帮助的还有:http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html

他们没有提到的类加载明确,但他们解释EDT访问策略是什么。

+0

我不是在谈论这里的EDT政策违规问题。美国东部时间的政策不是一个专断的规则,这是有原因的,我觉得它不完整。有关更多详细信息,请参阅mdma的答案。 – ddimitrov 2010-06-09 23:32:29