Re: An Executor-like structure providing more than threads
On Sun, 17 Jan 2010, Steven Simpson wrote:
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.
Exactly.
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).)
Right. The whole DownloadThread thing was really an attempt to pass the
HttpClient around the side of the Runnable interface.
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...
}
};
You could remove the uncertainty by putting the setup in the factory:
public Thread newThread(Runnable r) {
return new Thread(new Runnable() {
public void run() {
clientPool.set(new HttpClient(connMgr));
r.run();
}
});
}
That will run the HttpClient setup before heading into the code supplied
by the Executor, which is presumably some kind of internal class which
pulls Runnables off the Executor's queue.
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.
But it does require a ThreadLocal, which is on about the same level as
Thread subclasses and bletcherous casts, ie correct and legal, but
aesthetically displeasing.
tom
--
Why did one straw break the camel's back? Here's the secret: the million
other straws underneath it - it's all mathematics. -- Mos Def