Mau*_*ice 4 java spring multithreading servlet-dispatching
我制作了一个实现ApplicationListener<AuthenticationSuccessEvent>并应该记录 IP 地址的组件,IP 地址可以从HttpServletRequest. 显然这个组件在子线程中运行,因此我需要ThreadContextInheritable在DispatcherServlet组件上将属性设置为 true才能访问HttpServletRequest. (我尝试使用 RequestContextListener 但没有效果)。
在此处可以找到的 spring 文档中,当您设置ThreadContextInheritable为true.
警告:如果您访问的线程池被配置为潜在地按需添加新线程(例如 JDK ThreadPoolExecutor),请不要对子线程使用继承,因为这会将继承的上下文暴露给这样的池线程。
我的问题是:为什么将继承的上下文暴露给池线程是一件坏事?在这种情况下会出现什么问题?此外,它们是指使用ThreadPoolExecutor实例的子线程还是指使用ThreadPoolExecutor?创建的子线程?
InheritableThreadLocal 扩展了 ThreadLocal 并在我们需要在创建时自动将父线程本地属性值传递给子线程时使用。
当您每次都创建新的子线程并且不重用已创建的线程时,这种继承工作得非常好。
让我们考虑一个场景 - 我们有大小为 5 的线程池。当我们将前 5 个任务发送到线程池以处理每个任务时,会创建新线程,因此线程本地属性的继承工作得非常好。问题从第 6 个请求开始。当您发送第 6 个任务时,不会创建新线程,但会重用线程池中已创建的线程来处理它。在第 6 次请求时,父线程本地属性值不会被继承到子线程,因为我们不是在创建新线程而是重用池中已创建的线程。
这段代码片段将解释这一点 -
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DeadlyComboTest {
public static void main(String[] args) throws InterruptedException {
int noOfThreads = 5;
//Execution 1
int threadPoolSize = noOfThreads;
//Execution 2 : uncomment below line and comment line above
//int threadPoolSize = noOfThreads/2;
//1. create thread pool
ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
//2. create Inheritable thread local
InheritableThreadLocal<Object> value = new InheritableThreadLocal<>();
//3. create new command and execute using thread pool created in step 1
for (int i = 1; i <= noOfThreads; i++) {
value.set(i);
executor.execute(() -> printThreadLocalValue(value));
}
executor.shutdown();
}
private static void printThreadLocalValue(ThreadLocal<Object> value) {
System.out.println(Thread.currentThread().getName() + " = " + value.get());
}
}
Run Code Online (Sandbox Code Playgroud)
执行 1:noOfThreads = threadPoolSize = 5
OUTPUT: you may get the output in different sequence
pool-1-thread-1 = 1
pool-1-thread-2 = 2
pool-1-thread-3 = 3
pool-1-thread-4 = 4
pool-1-thread-5 = 5
Run Code Online (Sandbox Code Playgroud)
一切看起来都不错。在每个请求上都会创建新线程,并且子线程正确继承父线程的线程本地值。
执行 2:noOfThreads = 5 && threadPoolSize = noOfThreads/2 = 2 OUTPUT:
pool-1-thread-1 = 1
pool-1-thread-2 = 2
pool-1-thread-1 = 1
pool-1-thread-2 = 2
pool-1-thread-1 = 1
Run Code Online (Sandbox Code Playgroud)
仅对于前两个请求,线程本地继承工作正常,因为线程池大小为 2,因此对于前两个请求,新线程被创建和池化。第三个请求已经从池中创建的线程被重用,这些线程仍然具有旧线程的继承值。
执行 2 是一个真实的场景,池中的线程将被重用。
这将导致意外的信息泄漏,因为池中的工作线程/子线程会将一个线程的线程本地属性值泄漏到另一个线程。
如果您的业务逻辑依赖于这些属性值,您将很难跟踪错误。
这就是为什么 spring 文档警告不要使用线程池和 InheritableThreadLocal 的这种组合。
| 归档时间: |
|
| 查看次数: |
515 次 |
| 最近记录: |