Параллельный поток не устанавливает 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())