使用 ForkJoinPool Java 找不到 JAXB 类

问题描述

我使用 jaxb 进行解组,但是当我使用 ForkJoinPool execute() 方法时,我得到一个“java.log.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory”,但我确定我的运行时类路径中的存在,因为当我不使用 ForkJoinPool 时它会正常工作......你知道一个问题或解决方法吗?

我使用 Java 11

我的代码

ForkJoinPool commonPool = ForkJoinPool.commonPool();
        commonPool.execute(() -> {
            try {
                String messageFileContent = Files.readString(Paths.get(
                        this.getClass().getClassLoader().getResource("xml-to-process.xml").getPath()));

                JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
                Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
                // avoiding schema validation for more performance
                jaxbUnmarshaller.setSchema(null);
                UpdateWorkOrder updateWorkOrder = (UpdateWorkOrder) jaxbUnmarshaller.unmarshal(new StringReader(messageFileContent));
            } catch (Exception e) {
                e.printstacktrace();
            }
        });

这很奇怪,不是...?在 ForkJoinPool 的 execute() 之外,正确执行了解析。

错误是:

javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
with linked exception:
[java.lang.classNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(UnkNown Source)
at java.base/java.util.concurrent.ForkJoinTask.doExec(UnkNown Source)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(UnkNown Source)
at java.base/java.util.concurrent.ForkJoinPool.scan(UnkNown Source)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(UnkNown Source)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(UnkNown Source)
Caused by: java.lang.classNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(UnkNown Source)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(UnkNown Source)
at java.base/java.lang.classLoader.loadClass(UnkNown Source)
at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:92)
at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:125)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:230)

解决方法

如果 ForkJoinPool 使用与您的应用程序不同的类加载器,尤其是当您使用 Tomcat 之类的 Web 容器时,就会发生这种情况。

您可以尝试为 ForkJoinPool 设置线程工厂,并在该工厂内,将创建的线程的上下文类加载器设置为正确的应用程序类加载器。

,

虽然 NightShade 的回答完全描述了问题,但让我补充一些细节。

Servlet API 世界和并发世界非常不兼容:前者假设所有计算都在同一个线程上完成(ServletRequest#startAsync 是一个相对较新的补充)。因此,使用 Servlet API 的应用程序和库通常将对象附加到当前的 Thread,例如:

  • 执行请求的线程将应用程序的类加载器附加为“上下文类加载器”
  • Spring 将 RequestContextHolder 附加到当前线程(如 ThreadLocal),
  • Vaadin 通过 CurrentInstance 附加许多对象,
  • ...

这意味着每当您想在另一个线程上执行某些操作时,您都需要将所有这些对象复制到目标线程上。一个简单的方法是为您的 Runnable(和 Callable<T>)编写一个包装器,在开始执行之前设置环境:

public class WrappedRunnable implements Runnable {

   private final ClassLoader ccl;
   ... // other objects
   private final Runnable    runnable;

   public static Runnable wrap(final Runnable runnable) {
      if (runnable instanceof WrappedRunnable) {
         return runnable;
      }
      return new WrappedRunnable(runnable);
   }

   public WrappedRunnable(final Runnable runnable) {
      this.ccl = Thread.currentThread().getContextClassLoader();
      ... // other objects
      this.runnable = runnable;
   }

   @Override
   public void run() {
      final ClassLoader oldCcl = Thread.currentThread().getContextClassLoader();
      ... // save the value of other objects
      try {
         Thread.currentThread().setContextClassLoader(ccl);
         ... // set the value of other objects
         runnable.run();
      } finally {
         Thread.currentThread().setContextClassLoader(oldCcl);
         // restore the value of other objects
      }
   }
}

您也可以编写自己的 ExecutorService(您实际上将 ForkJoinPool 用作 ExecutorService)或 ForkJoinPool,它们会自动包装 Runnable .

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...