ThreadPoolExecutor的行为,keepAliveTime = 0,corePoolSize = 0

时间:2015-12-13 16:51:15

标签: java multithreading spring thread-safety threadpool

ThreadPoolExecutor' keepAliveTimecorePoolSize设置为0是否会为每项任务创建新的Thread?是否保证任何Thread都不会被重用于任何任务?

BTW我想将maximumPoolSize设置为100左右。我无法承受无限量的线程。如果我达到了线程的限制(例如100),我希望服务器回退到“同步”状态。模式(没有并行性)。请参阅ThreadPoolExecutor.CallerRunsPolicy

背景(仅在您对我的动机感兴趣时阅读):

我们有一个项目依赖于ThreadLocals的使用(例如我们使用Spring及其SecurityContextHolder)。我们希望并行调用10个后端系统。我们喜欢ThreadPoolExecutor.CallerRunsPolicy,它在调用程序线程中运行可调用,以防线程池及其任务队列已满。这就是我们希望使用ThreadPoolExecutor的原因。我无法更改项目不使用ThreadLocals,请不要建议这样做。

我正在考虑如何以最少的工作量去做。可以将SecurityContextHolder切换为使用InheritableThreadLocal而不是ThreadLocal。然后,在创建子线程时,将线程局部变量传递给子线程。唯一的问题是如何让ThreadPoolExecutor为每项任务创建新的Thread。将其keepAliveTimecorePoolSize设置为0会有效吗?我确定没有一个线程会被重用于下一个任务吗?我可以接受创建新Threads的性能影响,因为并行任务每个都花费更多时间。

考虑了其他可能的解决方案:

  1. 扩展ThreadPoolExecutor的{​​{1}}方法,并将execute参数包装到另一个Runnable command中,该Runnable会将线程本地记录到其最终字段中,然后将其初始化在调用目标run之前的command方法中。我认为这可能有用,但是维护的代码比我原来的问题解决方案要多得多。
  2. 在异步方法的参数中传递线程局部变量。这更加冗长。人们也会忘记这样做,安全上下文将在异步任务之间共享: - (
  3. 扩展ThreadPoolExecutor的{​​{1}}和beforeExecute方法,并使用反射复制线程本地。这需要大约50行丑陋的反射代码,我不确定它是多么安全。

1 个答案:

答案 0 :(得分:0)

不,这不行! ThreadPoolExecutor将您的Callable / Runnable包装到内部Worker对象中,并以runWorker()方式执行。这种方法的Javadoc说:

  

主要工作人员运行循环。从队列中反复获取任务并执行它们,同时处理许多问题:...

您还可以查看此方法的代码,并看到它在任务队列为空(或发生导致线程退出的不良事件)之前不会退出。

因此,将keepAliveTime设置为0不一定会在每个提交的任务上产生新线程。

你可能应该使用你的解决方案3,因为beforeExecute()afterExecute()方法正好用于处理ThreadLocals。

或者,如果您坚持为每项任务设置新主题,您可以查看Spring的SimpleAsyncTaskExecutor。它保证为每个任务创建一个新线程,并允许您设置并发限制,即相当于ThreadPoolExecutor#maxPoolSize