Re: An Executor-like structure providing more than threads
On 17/01/10 14:52, Lew wrote:
Steven Simpson wrote:
On 17/01/10 01:11, Tom Anderson wrote:
What if the resource needed to perform a task, the thing you wanted to
maintain a limited pool of, reuse, and provide shared access to, was
more than a Thread?
[...] But i really wanted to be able to use all the cool stuff in
ExecutorService, like getting Futures and having orderly shutdown, and
a properly controllable thread pool and so on. So what i did was
subclass Thread to add the other bits (the HttpClient and so on), and
then, in the tasks, do something like:
((DownloadThread)Thread.currentThread()).getHttpClient()
Would a ThreadLocal<HttpClient> be appropriate?
My noion based on the question and some of the answers given is that
the Runnable or Callable running from the thread pool should
instantiate a client locally from the separate client pool.
I thought the point was that you need at least one HttpClient per thread
(or you will have no more threads running than HttpClients), and you do
not need more than one per thread (spares are wasted). (This is what I
take from the items of the pool being 'more than a Thread'.) The
threads are already in a pool, so you may as well exploit the thread
pool policy for maintaining HttpClients, rather than trying to build a
parallel pool.
It won't need 'ThreadLocal' because the client reference will already
be local. The tangle comes from the idea that the client must be
associated with the thread at construction of the thread. I propose
that the client be acquired in the run method of the thread.
I am supposing that the OP is using one of the Executors statics to
build a thread pool, so he'll have to pass in a ThreadFactory, in order
to inject his DownloadThread subclass. The thread pool determines what
Runnable to pass to the thread, so he can't inject his own
client-acquiring code there. (He could supply his own Runnable to the
thread to create the client, and then run the pool's Runnable:
new ThreadFactory() {
public Thread createThread(final Runnable action) {
Runnable myAction = new Runnable() {
public void run() {
HttpClient client = new HttpClient(...);
action.run();
}
};
return new DownloadThread(myAction);
}
}
....but he still can't get that client reference into action.run() or
indeed the tasks' run()/call() methods, except by the way he's doing it
now (making it a field in DownloadThread).)
With the ThreadLocal, the first task that each thread executes will set
up the HttpClient, and subsequent tasks will re-use it:
// field with same lifetime as the ExecutorService
ThreadLocal<HttpClient> clientPool = new ThreadLocal<HttpClient>();
// in method creating new task
Runnable action = new Runnable() {
public void run() {
HttpClient client = clientPool.get();
if (client == null) {
client = new HttpClient(...);
clientPool.set(client);
}
// Do stuff with client...
}
};
I think that's cleaner, as it doesn't require a Thread subclass, so it
doesn't require a ThreadFactory, nor the 'bletcherous' cast to the subclass.
I am not fond of 'ThreadLocal'; I'm not a fan of global objects
generally.
What is global here? The ThreadLocal instance need not be any more
global than the thread pool it's associated with.
The value returned by ThreadLocal#get is also not a global - in terms of
its lifetime, it exists (approximately) for as long as the Thread's own
Runnable#run() is executing, just as a local variable of that method
would, except it is also visible to anything that is both called by that
method and has access to clientPool.
--
ss at comp dot lancs dot ac dot uk