Параллельный поток не устанавливает Thread.contextClassLoader после обновления tomcat

После обновления tomcat с 8.5.6 до 8.5.28 параллельный поток прекратил предоставлять потокам contextClassLoader:

Из-за этого Warmer::run не могу загрузить классы в нем.

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

Есть ли у вас какие-либо идеи о том, что Tomcat поставлял для contextClassLoaders для новых потоков?

ParallelStream использует ForkJoinPool в новейшей версии Tomcat.

2 ответа

Решение

Общий пул ForkJoin проблематичен и может быть причиной утечек памяти и того, что приложения могут загружать классы и ресурсы из других контекстов / приложений (потенциальная утечка безопасности, если ваш tomcat является мультитенантным). Смотрите этот отчет Tomcat Bugzilla.

В Tomcat 8.5.11 они исправили вышеуказанные проблемы, введяSafeForkJoinWorkerThreadFactory.java

Для того, чтобы ваш код работал, вы можете сделать следующее, которое предоставит явный ForkJoin и его фабрику рабочих потоков для 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())
Другие вопросы по тегам