I am making use of a third party FooClientImpl
Java class, which implements the FooClient
interface:
interface FooClient {
public FooOutput processFoo(FooInput inputData);
}
which processes data passed to it by communicating over a TCP socket on a particular port number on localhost
to a third-party, non-Java fooserver
process. Each inputData
instance can be processed standalone (i.e it's embarrassingly parallel). fooserver
is rather slow but just runs on a single core, so I'd like to be able to manually fire up multiple instances of it on separate ports , and have multiple FooClientImpl
instances to access it -- one for each server instance. So far this is easy.
However I'd also like to create a ConcurrentFooClientImpl
class which also implements the FooClient
interface, but under the hood, keeps track of a pool of FooClientImpl
instances or subclass instances (maybe 8), and processes the incoming data with the first unused instance after the .processFoo()
method is called (waiting until one is available if necessary). That way, other code could instantiate ConcurrentFooClientImpl
and it would look the same to the calling code, only faster.
I'm very new to threading, and I've walked through the Java Concurrency tutorial, but this use case seems somewhat different from the examples there.
I think I need to create a Thread
subclass FooClientImplThread
which wraps a FooClientImpl
instance. And I think I also should use Executors.newFixedThreadPool
to manage a fixed-size pool of clients, and that should maybe have a ThreadFactory
which produces instances of FooClientImplThread
. And then in ConcurrentFooClientImpl.processFoo()
I could wrap inputData
somehow in an implementation of Callable
to submit them to the pool. But these instances would need to get hold of a particular FooClient
instance so they could in turn call FooClientImplThread.processFoo()
and actually talk to the fooserver
running on the appropriate port. I guess they could call Thread.currentThread()
and cast the result to FooClientImplThread
. But intuitively that feels hacky to me (although I don't know if trust my intuition here), and I'm not sure whether it would work or whether it is sensible..
Am I on the right track with my possible solution here, or is there a more sane approach I should take?
Update: I ended up implementing a solution like the one described. Surprisingly, it mostly worked as expected, except of course I realised I hadn't thought through what would be needed for background processing while maintaining the outward-facing API. As far as I can tell, this is not possible in the one-at-at-time caller-triggered API I outlined above. It would need to be replaced by a producer-consumer pattern or something, which I ended up doing, albeit in a different way.