tomcat 升级后,并行流未设置 Thread.contextClassLoader

Parallel stream doesn't set Thread.contextClassLoader after tomcat upgrade

tomcat 从 8.5.6 升级到 8.5.28 后,并行流停止为线程提供 contextClassLoader:

因为Warmer::run无法在其中加载类。

warmers.parallelStream().forEach(Warmer::run);

您是否知道 Tomcat 为新线程的 contextClassLoader 提供了什么?

ParallelStream 在最新 Tomcat.

中使用 ForkJoinPool

Common ForkJoin pool 存在问题,可能导致内存泄漏以及应用程序能够从其他 contexts/applications 加载 类 和资源(潜在如果您的 tomcat 是多租户,则存在安全漏洞)。看到这个 Tomcat Bugzilla Report.

Tomcat 8.5.11 they had applied fix to the above issues by introducing SafeForkJoinWorkerThreadFactory.java

为了让您的代码工作,您可以执行以下操作,这将提供显式的 ForkJoin 及其 worker thread factory Stream.parallel() 执行。

ForkJoinPool forkJoinPool = new ForkJoinPool(NO_OF_WORKERS);
forkJoinPool.execute(() -> warmers.parallelStream().forEach(Warmer::run));

这救了我一命! 我必须这样做才能让它发挥作用:

private static class CustomForkJoinWorkerThread extends ForkJoinWorkerThread {
    CustomForkJoinWorkerThread(ForkJoinPool pool) {
        super(pool);
        setContextClassLoader(Thread.currentThread().getContextClassLoader());
    }
}

private ForkJoinPool createForkJoinPool() {
    return new ForkJoinPool(
            ForkJoinPool.getCommonPoolParallelism(),
            CustomForkJoinWorkerThread::new,
            null,
            false
    );
}


createForkJoinPool().submit(() -> stuff.parallelStream().doStuff())