面试题答案
一键面试不同线程池对执行上下文传递的影响
- ForkJoinPool:
- 特点:ForkJoinPool是Java 7引入的一种线程池,主要用于分治算法场景,它采用工作窃取算法提高线程利用率。
- 对执行上下文传递的影响:ForkJoinPool在任务执行时,会使用当前线程来执行子任务,在一定程度上能较好地维持执行上下文。例如,如果在主线程中设置了一些线程本地变量(
ThreadLocal
),当任务提交到ForkJoinPool后,如果是由当前主线程来执行任务(在工作队列未满等情况下),那么ThreadLocal
中的值可以正常访问。但如果是其他线程从工作队列中窃取任务执行,由于线程不同,ThreadLocal
的值可能无法正确传递。
- 自定义线程池:
- 特点:自定义线程池可以根据具体需求设置线程数量、队列类型、拒绝策略等参数,灵活性较高。
- 对执行上下文传递的影响:自定义线程池中的线程是独立创建和管理的。如果不做额外处理,不同线程之间的执行上下文是相互隔离的。例如,在主线程中设置的
ThreadLocal
变量,在线程池中的线程执行任务时,默认无法获取主线程中的ThreadLocal
值,因为它们是不同的线程实例。
确保上下文正确传递的方法
- 针对ForkJoinPool:
- 使用InheritableThreadLocal:
InheritableThreadLocal
类是ThreadLocal
的子类,它允许子线程继承父线程的ThreadLocal
值。在使用ForkJoinPool时,可以将需要传递的上下文数据放入InheritableThreadLocal
中。例如:
- 使用InheritableThreadLocal:
InheritableThreadLocal<String> context = new InheritableThreadLocal<>();
context.set("some context data");
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.submit(() -> {
String data = context.get();
System.out.println("Got context data in ForkJoinPool task: " + data);
return null;
});
- 针对自定义线程池:
- 使用装饰线程工厂:可以自定义一个线程工厂,在创建线程时,将主线程的上下文数据传递给新创建的线程。例如,结合
InheritableThreadLocal
和自定义线程工厂:
- 使用装饰线程工厂:可以自定义一个线程工厂,在创建线程时,将主线程的上下文数据传递给新创建的线程。例如,结合
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class ContextPropagatingThreadFactory implements ThreadFactory {
private final InheritableThreadLocal<String> context;
public ContextPropagatingThreadFactory(InheritableThreadLocal<String> context) {
this.context = context;
}
@Override
public Thread newThread(Runnable r) {
String contextValue = context.get();
Thread t = new Thread(() -> {
InheritableThreadLocal<String> localContext = new InheritableThreadLocal<>();
localContext.set(contextValue);
r.run();
});
return t;
}
}
public class Main {
public static void main(String[] args) {
InheritableThreadLocal<String> context = new InheritableThreadLocal<>();
context.set("important context");
ContextPropagatingThreadFactory factory = new ContextPropagatingThreadFactory(context);
ExecutorService executorService = Executors.newFixedThreadPool(5, factory);
executorService.submit(() -> {
String data = context.get();
System.out.println("Got context data in custom thread pool task: " + data);
});
executorService.shutdown();
}
}
- 使用
CompletableFuture
的上下文传递特性:CompletableFuture
提供了一些方法来处理上下文传递,例如thenApplyAsync
方法可以接受一个Executor
参数。可以将自定义的线程池与CompletableFuture
结合使用,并在CompletableFuture
的链式调用中,通过withContextCapture
方法来捕获上下文。例如:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
InheritableThreadLocal<String> context = new InheritableThreadLocal<>();
context.set("context data");
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture.supplyAsync(() -> {
return context.get();
}, executorService)
.thenApplyAsync(data -> {
System.out.println("Got context data in CompletableFuture task: " + data);
return data;
}, executorService)
.withContextCapture();
executorService.shutdown();
}
}
通过以上方法,可以在使用不同线程池时,尽可能确保执行上下文的正确传递。