7

I'd like to see if there's a good pattern for sharing a context across all classes and subthreads of a top-level thread without using InheritableThreadLocal.

I've got several top-level processes that each run in their own thread. These top-level processes often spawn temporary subthreads.

I want each top level process to have and manage it's own database connection.

I do not want to pass around the database connection from class to class and from thread to subthread (my associate calls this the "community bicycle" pattern). These are big top-level processes and it would mean editing probably hundreds of method signatures to pass around this database connection.

Right now I call a singleton to get the database connection manager. The singleton uses InheritableThreadLocal so that each top-level process has it's own version of it. While I know some people have problems with singletons, it means I can just say DBConnector.getDBConnection(args) (to paraphrase) whenever I need the correctly managed connection. I am not tied to this method if I can find a better and yet still-clean solution.

For various reasons InheritableThreadLocal is proving to be tricky. (See this question.)

Does anyone have a suggestion to handle this kind of thing that doesn't require either InheritableThreadLocal or passing around some context object all over the place?

Thanks for any help!


Update: I've managed to solve the immediate problem (see the linked question) but I'd still like to hear about other possible approaches. forty-two's suggestion below is good and does work (thanks!), but see the comments for why it's problematic. If people vote for jtahlborn's answer and tell me that I'm being obsessive for wanting to avoid passing around my database connection then I will relent, select that as my answer, and revise my world-view.

Community
  • 1
  • 1
Jeff Goldberg
  • 931
  • 1
  • 9
  • 20
  • Is the problem InheritableThreadLocal combined with ThreadPoolExecutor? – forty-two Jan 26 '12 at 13:15
  • +1 for'Boris Bikes' reference. – Martin James Jan 26 '12 at 14:45
  • 1
    Passing some object around? What, like modern programming? / `InheritableThreadLocal` is the work of the devil. – Tom Hawtin - tackline Jan 26 '12 at 15:01
  • @forty-two: ThreadPoolExecutor creates permanent threads which it "reuses" to run each Runnable that you pass to the executor. This means that instead of grabbing the InheritableThreadLocal of the process which sent the Runnable to the ThreadPoolExecutor, it actually ends up using the InheritableThreadLocal of whatever process first utilized that thread from the ThreadPoolExecutor thread pool. Took me forever to debug that. See the linked question above for more details. – Jeff Goldberg Jan 26 '12 at 16:52

5 Answers5

3

I haven't tested this, but the idea is to create a customized ThreadPoolExecutor that knows how to get the context object and use #beforeExecute() to transfer the context object to the thread that is going to execute the task. To be a nice citizen, you should also clear the context object in #afterEXecute(), but I leave that 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
    }

}
}
forty-two
  • 12,204
  • 2
  • 26
  • 36
  • I've tried this in my test application and it works. It seems, however, like it's a proper answer to my other question (http://stackoverflow.com/questions/9012371/using-inheritablethreadlocal-with-threadpoolexecutor-or-a-threadpoolexecut) and not this question. This question is seeking a way to accomplish the behavior of InheritableThreadLocal without resorting to InheritableThreadLocal. Your answer gets InheritableThreadLocal to work with a ThreadPoolExecutor but doesn't avoid it. Unless I am misunderstanding something...? – Jeff Goldberg Jan 27 '12 at 16:08
  • You don't need an InheritableThreadLocal, just a normal ThreadLocal. – forty-two Jan 27 '12 at 16:37
  • Okay, I see where you are coming from but I'm imagining a more abstract scenario with multiple layers. Let's say the Runnables that are being sent to this XyzThreadPoolExecutor by the top-level processes are also going to spit out their own sub-threads. This solves the process of getting the top-level process context to the right Runnables in the executor, but you are still going to need to be using an InheritableThreadLocal at the top-level for it to trickle down to the sub-threads of the Runnables. – Jeff Goldberg Jan 27 '12 at 18:22
  • But there would be no sub threads. Every runnable that creates new Runnables will transfer its current context, provided that the created Runnable is excuted by a XyzThreadPoolExecutor. Or do you have a mix of "free" threads and executors? – forty-two Jan 27 '12 at 18:37
  • Right! Of course. I suppose it's possible that there is a mix of "free" threads and executors, but that should be in the control of the person designing the system. It's not likely that a third party package would both spins off "free" threads and also expect to utilize your ThreadLocal variable. – Jeff Goldberg Jan 27 '12 at 19:26
  • One note: If your top-level processes are kicked off from one main class, you can NOT use this XyzThreadPoolExecutor to run those top-level processes. Otherwise all processes will have the same ThreadLocal as the main context rather than a different context per top-level process. They will have to be kicked off in "free" threads, and then all other sub-Runnables should go through XyzThreadPoolExecutor. – Jeff Goldberg Jan 27 '12 at 19:37
  • Granted. And this is perhaps a good argument against this solution, as it is not very transparent. I would recommend using a concurrency construct that better fits the true nature of the problem--see my answer to your [linked question](http://stackoverflow.com/questions/9012371/using-inheritablethreadlocal-with-threadpoolexecutor-or-a-threadpoolexecut). – forty-two Jan 27 '12 at 19:51
3

the "community bicycle" solution (as you call it) is actually much better than the global (or pseudo global) singleton that you are currently using. it makes the code testable and it makes it very easy to choose which classes use which context. if done well, you don't need to add the context object to every method signature. you generally ensure that all the "major" classes have a reference to the current context, and that any "minor" classes have access to the relevant "major" class. one-off methods which may need access to the context will need their method signatures updated, but most classes should have the context available through a member variable.

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
2

As a ThreadLocal is essentially a Map keyed on your thread, couldn't you implement a Map keyed on your thread name? All you then need is an effective naming strategy that meets your requirements.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • I think that's a good idea, and I've considered it as an option. You still have a similar problem if you use a ThreadPoolExecutor because all tasks will run in one of the executor's pooled threads. But the thread name can still be managed using the answer from this question: http://stackoverflow.com/questions/8523453/with-threadpoolexecutor-how-to-get-the-name-of-the-thread-running-in-the-thread – Jeff Goldberg Feb 05 '12 at 22:29
1

As a Lisper, I very much agree with your worldview and would consider it a shame if you were to revise it. :-)

If it were me, I would simply use a ThreadGroup for each top-level process, and associate each connection with the group the caller is running in. If using in conjunction with thread pools, just ensure the pools use threads in the correct thread group (for instance, by having a pool per thread group).

Example implementation:

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 then simply make sure to run all your threads in a proper ProcessContext, you can get a CachedConnection anywhere by calling ProcessContext.getContext(CachedConnection.class).

Of course, as mentioned above, you would have to make sure that any other threads you may delegate work to also run in the correct ProcessContext, but I'm pretty sure that problem is inherent in your description -- you would obviously need to specify somehow which one of multiple contexts your delegation workers run in. If anything, it could be conceivable to modify 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));
    }
}

That way, you could use ProcessContext.wrapInContext() to pass a Runnable which, when run, inherits its context from where it was created.

(Note that I haven't actually tried the above code, so it may well be full of typos.)

Dolda2000
  • 25,216
  • 4
  • 51
  • 92
1

I would not support your world-view and jthalborn's idea on the count that its more testable even.

Though paraphrasing first what I have understood from your problme statement is like this.

  1. There are 3 or 4 top-level processes (and they are basically having a thread of their own). And connection object is what is diffrenet in them.
  2. You need some basic characteristic of Connection to be set up and done once.
  3. The child threads in no way change the Connection object passe to them from top-level threads.

Here is what I propose, you do need the one tim,e set-up of you Connection but then in each of your top-level process, you do 1) further processing of that Connection 2) keep a InheriatbleThreadLocal (and the child process of your top-level thread will have the modified connection object. 3) Pass these threasd implementing classes. MyThread1, MyThread2, MyThread3, ... MyThread4 in the Executor. (This is different from the other linked question of yours that if you need some gating, Semaphore is a better approach)

Why I said that its not less testable than jthalborn's view is that in that case also you anyway again needs to provide mocked Connection object. Here too. Plus conecptually passing the object and keeping the object in ThreadLocal is one and the same (InheritableThreadLocal is a map which gets passed by java inbuilt way, nothing bad here I believe).

EDIT: I did keep in account that its a closed system and we are not having "free" threads tempring with connection

manocha_ak
  • 904
  • 7
  • 19