1. 程式人生 > >如何解決ThreadLocal與ThreadPool的小矛盾?

如何解決ThreadLocal與ThreadPool的小矛盾?

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();
	}