如何安全地解决此Java上下文类加载器问题?

我的数百名用户中只有一个在启动Java桌面应用程序时遇到问题。 只有三分之一的时间才开始。 另外三分之二的时间在启动时抛出NullPointerException:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at java.util.Hashtable.put(Hashtable.java:394) at javax.swing.JEditorPane.registerEditorKitForContentType(JEditorPane.java:1327) at javax.swing.JEditorPane.registerEditorKitForContentType(JEditorPane.java:1309) at javax.swing.JEditorPane.loadDefaultKitsIfNecessary(JEditorPane.java:1387) at javax.swing.JEditorPane.getKitTypeRegistry(JEditorPane.java:1344) at javax.swing.JEditorPane.getEditorKitClassNameForContentType(JEditorPane.java:1340) at javax.swing.JTextPane.(JTextPane.java:76) at myapp.Launcher$1.run(Launcher.java:13) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209) at java.awt.EventQueue.dispatchEvent(EventQueue.java:633) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188) at java.awt.EventDispatchThread.run(EventDispatchThread.java:122) 

我已经按照堆栈跟踪找到原因

 Thread.currentThread().getContextClassLoader() 

在JEditorPane中返回null。

谷歌搜索显示,这是一个零星的,非常罕见的,神秘的问题,影响了一些人。

我的问题是,作为一种解决办法,我能做些什么? 如果我在创建EditorPane之前调用它,这可能会有效:

 Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader()); 

但我并不像我想的那样真正理解类加载器(我试图更好地理解它们)。 我觉得改变EDT中的contextClassLoader可能会产生不良影响。

任何想法我能做什么?

编辑:我与熟悉Java ClassLoaders的人有一些通信。 这似乎是一个模糊的ClassLoader竞争条件。 也就是Java中的一个bug。

 Thread.currentThread().getContextClassLoader() 

如果JEditorPane.registerEditorKitForContentType中的代码未在上面的代码中检查null返回值,则这是JEditorPane的错误。 请注意, MyClass.class.getClassLoader() 也可能返回null 。 您唯一可以依赖的是系统ClassLoader 。

为调用设置上下文ClassLoader的模式通常如下所示:

 Thread thread = Thread.currentThread(); ClassLoader old = thread.getContextClassLoader(); thread.setContextClassLoader(fooClassLoader); try { // do call that depends on context ClassLoader } finally { thread.setContextClassLoader(old); } 

应该通过setContextClassLoader设置的值将取决于使用它的代码的意图以及您正在运行的ClassLoader框架的设计。

在独立的应用程序中,您可能只需使用此ClassLoader (将ref传递给当前类)即可:

 private ClassLoader findClassLoaderForContext(Class c) { ClassLoader context = Thread.currentThread().getContextClassLoader(); ClassLoader me = c.getClassLoader(); ClassLoader system = ClassLoader.getSystemClassLoader(); return (context == null) ? (me == null) ? system : me : context; } 

在ClassLoader敏感的插件框架(Java EE服务器将是一个主要的例子)中,理解加载方案的性质和用法是值得的。