在引入Java Memory Model之后,Swing指南被更改为声明任何Swing组件需要在EDT上实例化以避免未发布的实例状态。
我在任何地方都找不到的是,类加载是否也被强制要求在EDT中,还是我们可以在后台线程中预加载关键的Swing类? Sun / Oracle对此有何正式声明?是否有任何已知的类保持非线程安全静态,因此需要在EDT上加载?
澄清Nemi的问题:这是一个实际问题。我们的应用程序启动时间的相当大一部分用于在EDT上进行类加载和字体/图像加载。其中大部分可归因于Swing和相关库。
这是som背景:正如许多其他Swing应用程序一样,在启动时我们预先构建了许多表单,以使UI更具响应性。在分析之后,我们发现表单构造的实际时间相对较快 - 所有类和字体的加载速度都很慢(企业设置中的磁盘读取速度慢,具有按访问病毒扫描程序,监视扫描程序,审计跟踪器和上帝知道HDD驱动程序上还有什么内容。
我们尝试在后台线程中构造相同的表单(违反Swing的规则)然后扔掉它们。一旦我们完成,我们在EDT上构建相同的表单,这比加载所有类和磁盘缓存中的任何其他文件要快得多。它适用于我们,我们可能会继续这样做,除非发生一些非常糟糕的事情。
我要问的是这是一种安全的做法,一种好的做法还是一种黑客行为?
答案 0 :(得分: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中完成”) - 这是一个自定义违反EDT访问政策时失败的RepaintManager
。
您可能会发现这也很有用:http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html
他们没有明确提及类加载,但他们确实解释了EDT访问策略的内容。
答案 1 :(得分:2)
证据似乎表明它是安全的 - 但是再次,正如ddimitrov在评论中所说的那样 - 由于未发布的更改而没有找到细微的线程错误,因为典型的机器只有几个内核,并且L2 / L3缓存是共享的。 (L1缓存是每个核心,但通常非常小。)
如果你想保证由于后台类加载没有问题,那么坚持在ETD上加载类可能是最安全的。要维护实时UI,请创建一个自定义类加载器,该加载器还会在加载每个类之间泵送事件。 (依赖性被重新加载,所以延迟只会在加载一个类的持续时间内。)假设这个类加载器与你的应用程序一起打包,那么它可以简单地将所有类加载推迟到它的类加载器。
或者,辅助事件队列可以在不同的线程上运行(例如模态对话框和spin库)。这意味着Swing可以在任何线程上运行,只要它只在一个线程上运行,并且意味着它必须是更新一致的(或者我们到目前为止都非常幸运!)在此基础上,你可以在主EDT上加载您的类,并启动辅助EDT以抽取UI事件,保持UI响应 - 与模式对话框功能相同。 Spin实用程序将为您提供EDT事件,或者您可以手动生成新的EDT。
答案 2 :(得分:0)
虽然你在技术上是正确的 - 我从来没有听说过在不同的线程中渲染表格的问题,只要你没有对它们做任何事情,除了EDT(根据太阳的原始版本)准则)。
那些曾经是太阳的指导方针,但Sun改变它们是按照你的指定 - 所以我确定有人发现或创造了一个潜在的冲突,但我也确定它会很难被击中,因为应用仍然工作,几乎没有人遵循这些准则。
关于你的问题,我确信你可以加载这些类,只要你没有实例化任何对象,并且仍然在最严格的指导范围内。
EDT仅用于避免线程冲突,因为Swing是单线程的(按设计)。如果您只是加载类而不是实例化任何类,那么您就无法解决任何线程问题。