Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using InheritableThreadLocal with ThreadPoolExecutor -- or -- a ThreadPoolExecutor that doesn't reuse threads

Tags:

I am trying to use both InheritableThreadLocal and a ThreadPoolExecutor.

This breaks down because ThreadPoolExecutor reuses threads for each pool (it is a pool, after all), meaning the InheritableThreadLocal doesn't work as expected. Now the problem seems obvious to me, but it was particularly snarly to track down.

I use InheritableThreadLocal so that each of several top-level processes has its own database connection for itself and any sub-processes it spawns. I don't just use one shared connection pool because each top-level process will do a lot of multi-step work with its connection before committing to the database and/or preparing a lot of PreparedStatements that are used over and over.

I use a shared ThreadPoolExecutor between these top-level processes because there are certain behaviors that need to be gated. e.g. Even though I might have 4 top-level processes running, I can only have any one process writing to the database at a time (or the system needs to gate on some other shared resource). So I'll have the top-level process create a Runnable and send it to the shared ThreadPoolExecutor to make sure that no more than one (or two or three as the case may be) are running at the same time across the entire system.

The problem is that because the ThreadPoolExecutor reuses its threads for the pools, the InheritableThreadLocal is picking up the original value that was run in that pool rather than the value that was in the top-level process which sent the Runnable to the ThreadPoolExecutor.

  • Is there any way to force the worker pool in the ThreadPoolExecutor to use the InheritableThreadLocal value that was in the context of the process which created the Runnable rather than in the context of the reused thread pool?

  • Alternatively, is there any implementation of ThreadPoolExecutor that creates a new thread each time it starts a new Runnable? For my purposes I only care about gating the number of simultaneously running threads to a fixed size.

  • Is there any other solution or suggestion people have for me to accomplish what I've described above?

(While I realize I could solve the problem by passing around the database connection from class to class to subthread to subthread like some kind of community bicycle, I'd like to avoid this.)

There is a previous question on StackOverflow, InheritableThreadLocal and thread pools, that addresses this issue as well. However, the solution to that problem seems to be that it's a poor use case for InheritableThreadLocal, which I do not think applies to my situation.

Thanks for any ideas.

like image 646
Jeff Goldberg Avatar asked Jan 26 '12 00:01

Jeff Goldberg


People also ask

Does Python ThreadPoolExecutor reuse threads?

Heterogeneous tasks, not homogeneous tasks. Reuse threads, not single use. Manage multiple tasks, not single tasks.

What is a ThreadPoolExecutor and why is it necessary?

ThreadPoolExecutor is an ExecutorService to execute each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods. It also provides various utility methods to check current threads statistics and control them.

How do you prevent ThreadPoolExecutor?

You can call the cancel() function on the Future object to cancel the task before it has started running. If your task has already started running, then calling cancel() will have no effect and you must wait for the task to complete.

What is the difference between thread and ThreadPool?

A thread pool is - as the name suggests - a pool of worker threads which are always running. Those threads then normally take tasks from a list, execute them, then try to take the next task. If there's no task, the thread will wait.


1 Answers

using InheritedThreadLocal is almost surely wrong. Probably you'd have not asked the question if you can fit that bizarre tool. First and foremost it's horribly leak-prone and often the value(s) escapes in some totally strange threads.

As for the Runnable being associate w/ a context. Override publicvoid execute(Runnable command) of the ExecutorPool and wrap the Runnable withing some context carrying the value you want in the first place from the InheritedThreadLocal.

The wrapping class shall look something like

class WrappedRunnable extends Runnable{
  static final ThreadLocal<Ctx> context=new ThreadLocal<Ctx>();
  final Runnable target;
  final Ctx context;
  WrappedRunnable(Ctx context, Runnable target){...}

  public void run(){
    ctx.set(context);
    try{ 
      target.run();
    }finally{
      ctx.set(null);//or ctx.remove()
    }
  }
}

Alternatively, is there any implementation of ThreadPoolExecutor that creates a new >thread each time it starts a new Runnable? For my purposes I only care about gating the >number of simultaneously running threads to a fixed size.

While truly bad from performance point of view, you can implement your own, basically you need only execute(Runnable task) method for the Executor that spawns new thread and starts it.

like image 104
bestsss Avatar answered Oct 07 '22 18:10

bestsss