如何解決ThreadLocal與ThreadPool的小矛盾?
阿新 • • 發佈:2019-01-30
ThreadLocal一個很有用的場景:收到請求,框架解析出使用者User物件,儲存到ThreadLocal中,然後Controller、Service中就可以直接從ThreadLocal中來獲取User物件了。大部分時候,這已經足夠了,直到遇到了ThreadPool。ThreadPool中的執行緒是不會被銷燬的,而且很明顯跟主執行緒不是同一個執行緒,那麼主執行緒中的ThreadLocal是無法直接傳遞到ThreadPool的執行緒裡面的!再ThreadPool的場景下,顯然InheritableThreadLocal是無用武之地的,下面我們就來簡單的封裝下。
這是我們的用來非同步執行任務的ThreadPoolUtil:
public class ThreadPoolUtil { private final ExecutorService executor; private static ThreadPoolUtil instance = new ThreadPoolUtil(); private ThreadPoolUtil() { this.executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); } public static ThreadPoolUtil getInstance() { return instance; } public static <T> Future<T> execute(final Callable<T> runnable) { return getInstance().executor.submit(runnable); } public static Future<?> execute(final Runnable runnable) { return getInstance().executor.submit(runnable); } }
既然無法直接從主執行緒傳遞到ThreadPool裡面的執行緒,那就只能用引數來傳遞了:
public static abstract class ParamRunnable<T> implements Runnable{ private T param; public ParamRunnable(Supplier<T> paramSupplier) { if(paramSupplier != null) { this.param = paramSupplier.get(); } } @Override public void run() { run(param); } public abstract void run(T param); }
我們建立Runnable的子類,在建構函式中把引數傳遞進去,這裡傳遞一個Supplier而非引數本身,引數由Supplier提供出來,在run()方法中呼叫abstract的run()同時把引數傳遞出去。同理我們繼續寫一個ParamCallable:
public static abstract class ParamCallable<R,P> implements Callable<R>{
private P param;
public ParamCallable(Supplier<P> paramSupplier) {
if(paramSupplier != null) {
this.param = paramSupplier.get();
}
}
@Override
public R call() {
return call(param);
}
public abstract R call(P param);
}
看一下如何來使用:
public static class User{
private int id;
public User() {}
public User(int id) {
this.id= id;
}
public String toString() {
return "User [id=" + id + "]";
}
}
public static class UserProvider{
private static ThreadLocal<User> userHolder = new ThreadLocal<User>();
public static void setUser(User user) {
userHolder.set(user);
}
public static User getUser() {
return userHolder.get();
}
}
public static void main(String[] args)throws Exception {
UserProvider.setUser(new User(1));
System.out.println(UserProvider.getUser());
ThreadPoolUtil.execute(new ParamRunnable<User>(UserProvider::getUser) {
@Override
public void run(User user) {
System.out.println(user);
}
});
System.in.read();
}