How to use a common context for a top-level process / thread without using InheritableThreadLocal?

I would like to see if there is a good context sharing template in all classes and sub-threads of a top-level thread without using InheritableThreadLocal .

I have several top-level processes, each of which works in its own thread. These top-level processes often generate temporary substreams.

I want each top-level process to have and manage its own connection to the database.

I don’t want to transfer the database connection from class to class and from stream to subtitle (my assistant calls this the “bicycle community template”). These are large top-level processes, and that would mean editing, probably, hundreds of method signatures to transfer this connection to the database.

Now I am calling singleton to get the database connection manager. Singleton uses InheritableThreadLocal so that each top-level process has its own version. Although I know that some people have single-player problems, this means that I can just say DBConnector.getDBConnection(args) (rephrase) whenever I need a properly managed connection. I am not attached to this method if I can find a better and cleaner solution.

For various reasons, InheritableThreadLocal proves to be complicated. (See this question .)

Does anyone have a suggestion to handle things that don't require either InheritableThreadLocal , or skip any context object everywhere?

Thanks for any help!


Update: I managed to solve the immediate problem (see the related question), but I still would like to hear about other possible approaches. a forty-two sentence is good and works (thanks!), but see comments on why this is problematic. If people vote for jtahlborn's answer and tell me that I am obsessive so as not to skip around my database connection, then I will soften, choose this as my answer and reconsider my worldview.

+7
source share
5 answers

I have not tested this, but the idea is to create a personalized ThreadPoolExecutor that knows how to get the context object and use #beforeExecute () to transfer the context object to the thread that will perform the task. To be a good citizen, you must also clear the context object in #afterEXecute (), but I leave this as an exercise.

 public class XyzThreadPoolExecutor extends ThreadPoolExecutor { public XyzThreadPoolExecutor() { super(3, 3, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new MyThreadFactory()); } @Override public void execute(Runnable command) { /* * get the context object from the calling thread */ Object context = null; super.execute(new MyRunnable(context, command)); } @Override protected void beforeExecute(Thread t, Runnable r) { ((MyRunnable)r).updateThreadLocal((MyThread) t); super.beforeExecute(t, r); } private static class MyThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { return new MyThread(r); } } private class MyRunnable implements Runnable { private final Object context; private final Runnable delegate; public MyRunnable(Object context, Runnable delegate) { super(); this.context = context; this.delegate = delegate; } void updateThreadLocal(MyThread thread) { thread.setContext(context); } @Override public void run() { delegate.run(); } } private static class MyThread extends Thread { public MyThread(Runnable target) { super(target); } public void setContext(Object context) { // set the context object here using thread local } } } 
+3
source

The "community bicycle" solution (as you call it) is actually much better than the global (or pseudo-global) singleton that you are currently using. this makes the code testable, and it is very easy to choose which classes use this context. if everything is done well, you do not need to add a context object to each method signature. you generally warrant that all "main" classes have a reference to the current context and that any "minor" classes have access to the corresponding "main" class. one-time methods that may need access to the context will need to update the signatures of their methods, but most classes must have a context accessible through a member variable.

+3
source

ThreadLocal essentially has a Map associated with your thread, could you implement a Map with a key in the name of your thread? All you need is an effective naming strategy to suit your needs.

+2
source

As a Lispper, I very much agree with your worldview and will consider it a shame if you reconsider it. :-)

If it were me, I would simply use ThreadGroup for each top-level process and associate each connection with the group in which the caller works. If you use with thread pools, just make sure that the pools use threads in the correct thread group (for example, having a pool in each thread group).

Implementation Example:

 public class CachedConnection { /* Whatever */ } public class ProcessContext extends ThreadGroup { private static final Map<ProcessContext, Map<Class, Object>> contexts = new WeakHashMap<ProcessContext, Map<Class, Object>>(); public static T getContext(Class<T> cls) { ProcessContext tg = currentContext(); Map<Class, Object> ctx; synchronized(contexts) { if((ctx = contexts.get(tg)) == null) contexts.put(tg, ctx = new HashMap<Class, Object>()); } synchronized(ctx) { Object cur = ctx.get(cls); if(cur != null) return(cls.cast(cur)); T new_t; try { new_t = cls.newInstance(); } catch(Exception e) { throw(new RuntimeException(e)); } ctx.put(cls, new_t); return(new_t); } } public static ProcessContext currentContext() { ThreadGroup tg = Thread.currentThread().getThreadGroup(); while(true) { if(tg instanceof ProcessContext) return((ProcessContext)tg); tg = tg.getParent(); if(tg == null) throw(new IllegalStateException("Not running in a ProcessContext")); } } } 

If you just simply run all your threads in the correct ProcessContext , you can get CachedConnection anywhere by calling ProcessContext.getContext(CachedConnection.class) .

Of course, as mentioned above, you will need to make sure that any other threads you can delegate also work in the correct ProcessContext, but I'm sure the problem is inherent in your description - you obviously need to specify one of several ways contexts that your delegate staff have encountered. In any case, one could modify the ProcessContext as follows:

 public class ProcessContext extends ThreadGroup { /* getContext() as above */ private static final ThreadLocal<ProcessContext> tempctx = new ThreadLocal<ProcessContext>(); public static ProcessContext currentContext() { if(tempctx.get() != null) return(tempctx.get()); ThreadGroup tg = Thread.currentThread().getThreadGroup(); while(true) { if(tg instanceof ProcessContext) return((ProcessContext)tg); tg = tg.getParent(); if(tg == null) throw(new IllegalStateException("Not running in a ProcessContext")); } } public class RunnableInContext implements Runnable { private final Runnable delegate; public RunnableInContext(Runnable delegate) {this.delegate = delegate;} public void run() { ProcessContext old = tempctx.get(); tempctx.set(ProcessContext.this); try { delegate.run(); } finally { tempctx.set(old); } } } public static Runnable wrapInContext(Runnable delegate) { return(currentContext().new RunnableInContext(delegate)); } } 

Thus, you can use ProcessContext.wrapInContext() to pass a Runnable that, when launched, inherits its context from where it was created.

(Please note that I have not actually tried the above code, so it could very well be filled in typos.)

+1
source

I would not support your idea of ​​worldview and jthalborn that it is even more verifiable.

Although at first paraphrasing what I understood from your problme statement, it is.

  • There are 3 or 4 top-level processes (and they basically have their own thread). And the connection object is what differs in them.
  • You will need a basic connection characteristic that needs to be configured and performed once.
  • Child streams in no way alter the path of the Connection object to them from top-level streams.

Here is what I suggest, you need one time, setting up your connection, but then in each of the top-level processes you will perform 1) further processing of this connection 2) save InheriatbleThreadLocal (and the child of your top-level thread will have a modified connection object. 3) Pass these threasd implementation classes. MyThread1, MyThread2, MyThread3, ... MyThread4 in the Artist. (This is different from the other question related to you that if you need some kind of gating, Semaphore is the best approach)

Why I said that his no less testable than jthalborn opinion is that in this case, you still need to make fun of the Connection object. Here too. Plus, passing the object and saving the object to ThreadLocal is one and the same (InheritableThreadLocal is a map that is transmitted by java in the built-in way, nothing bad here, I think).

EDIT: I remember that this is a closed system, and we do not have "free" pace with connecting

+1
source

All Articles