好了!new方式创建的线程池搞定,接下来看看Executors.xxx怎么玩,我们可不可以自己写个ProxyExecutors,使用asm把所有Executors.xxx都替换成ProxyExecutors.xxx,然后把其中new ThreadPool都换成new BaseThreadPool呢?
object ProxyExecutors {@JvmStaticfun newFixedThreadPool(nThreads: Int): ExecutorService {return BaseThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,LinkedBlockingQueue())}@JvmStaticfun newCachedThreadPool(): ExecutorService {return BaseThreadPoolExecutor(0, Int.MAX_VALUE,60L, TimeUnit.SECONDS,SynchronousQueue())}@JvmStaticfun newScheduledThreadPool(corePoolSize: Int,threadFactory: ThreadFactory?): ScheduledExecutorService {return BaseScheduledThreadPoolExecutor(corePoolSize, threadFactory)}}对于某些线程池确实没啥问题,但当你继续写下去时,会出问题,比如:
@JvmStaticfun newSingleThreadScheduledExecutor(): ScheduledExecutorService {return BaseDelegatedScheduledExecutorService(ScheduledThreadPoolExecutor(1))}DelegatedScheduledExecutorService是Executors的私有内部类,没办法方便的写出BaseDelegatedScheduledExecutorService 。所以继续观察下,发现所有创建线程池方法都返回的都是ExecutorService或ScheduledExecutorService,既然这么统一,并且这两个都是接口,那我们就使用动态代理吧!通过代理,就可以拿到接口的各种方法以及方法参数,然后为所欲为 。以ExecutorService接口为例:
object ProxyExecutors {@JvmStaticfun newFixedThreadPool(nThreads: Int): ExecutorService {return proxy(Executors.newFixedThreadPool(nThreads))}}private fun proxy(executorService: ExecutorService): ExecutorService {if (executorService is ThreadPoolExecutor) {// 这里和BaseThreadPoolExecutor一样,设置ThreadFactory为了尽早获取线程信息和线程池建立联系,而不用等到run时executorService.threadFactory = BaseThreadFactory(executorService.threadFactory,toObjectString(executorService))}val handler = ProxyExecutorService(executorService)return Proxy.newProxyInstance(executorService.JAVAClass.classLoader,AbstractExecutorService::class.java.interfaces,handler) as ExecutorService}// 这里使用java是因为kotlin在调用java变长参数方法时有坑public class ProxyExecutorService implements InvocationHandler {private ExecutorService executor;private String poolName = null;ProxyExecutorService(ExecutorService executor) {this.executor = executor;poolName = TrackerUtils.toObjectString(executor);// 初始化时获取线程池信息String createStack = TrackerUtils.getStackString(false);// 省略部分代码}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 因方法数众多,并且被代理的各类中方法也不一致// 所以被调用方法中只要含有Runnable、Callable类型的参数,都替换成PoolRunnable代理if (args != null) {String callStack = TrackerUtils.getStackString(true);for (int i = 0; i < args.length; i++) {Object arg = args[i];if ((arg instanceof Runnable || arg instanceof Callable) && !(arg instanceof PoolRunnable)) {// execute submit 等情况PoolRunnable any = new PoolRunnable(arg, callStack, poolName);// 替换方法参数args[i] = any;} else if (arg instanceof Collection && !((Collection) arg).isEmpty()) {// invokeAny invokeAll 等情况Iterator iter = ((Collection) arg).iterator();ArrayList<PoolRunnable> taskList = new ArrayList<>();boolean allOk = iter.hasNext();while (iter.hasNext()) {Object it = iter.next();if (it instanceof Runnable || it instanceof Callable) {if (it instanceof PoolRunnable) {taskList.add((PoolRunnable) it);} else {taskList.add(new PoolRunnable(it, callStack, poolName));}} else {allOk = false;break;}}if (allOk) {// 替换方法参数args[i] = taskList;}}}}if (method.getName().equals("shutdown") || method.getName().equals("shutdownNow")) {ThreadInfoManager.getINSTANCE().shutDownPool(poolName);}return method.invoke(executor, args);}}在invoke方法中,我们把所有参数为Runnable或Callable的都替换成自己的PoolRunnable,后面和new创建线程池的套路一样,在PoolRunnable的run()或call()方法中进行线程与堆栈的关联 。完美!
但是考虑一个问题,如果有人不幸写出这样的代码,会发生什么呢?
val pool = Executors.newFixedThreadPool(3) as ThreadPoolExecutor对了,会crash,因为我们动态代理后代理对象变成了ExecutorService,没办法向下转型成ThreadPoolExecutor,这也是动态代理的一个缺点,只能代理接口,如果一个类A除了实现接口还有很多自己的方法,动态代理对这些方法是无能为力的,代理后对象只是接口的实例,无法转成类A 。这个问题可以使用cglib/javassist库解决,这里就不展开了 。为了保险起见,我们可以把能用Base替换的都替换掉,只有类似newSingleThreadScheduledExecutor()这种不能用Base替换的才用动态代理:
推荐阅读
- 国产操作系统银河麒麟使用了开源内核,未来会有被卡脖子的风险吗
- 小米扫地机器人掉线了怎么设置 小米扫地机器人显示离线怎么办
- 工人体育场会改造成啥样?效果图来了
- 汽车排气管一根和两根有啥区别?是越多越好吗?专家:能别傻了吗
- 周公解梦梦见骨头 梦见自己骨头出来了怎么回事
- 梦见哑巴说话了是什么意思 梦见哑巴会说话咋回事
- 岳云鹏面馆一碗面要106元,为何却无人吐槽?看到食物后闭嘴了
- 梦到厕所墙倒了 梦见厕所的一面墙倒了梦见电打死耳朵流血好不好
- 榴莲肉坏了是什么口感 榴莲柄烂了里面肉会烂吗
- 梦见自己拉屎拉到裤裆里是什么意思 梦到把屎拉在裤裆里了
