The java.util.concurrent.ExecutorService and it’s implementations are great to take advantage of today’s multicore systems. You can submit a task from Thread A and it will be queued until a (worker) Thread B from the ExecutorService takes it and calculates the result (this is also an example of how the Producer-Consumer-Pattern can be implemented, but that is not the reason of this post ;-). When a task is submitted by a thread, the thread returns immediately holding a java.util.concurrent.Future returned by the ExecutorService’s submit-methods. There is no blocking operation until someone calls get() on the returned Future (and the calculation of the Future has not yet been completed) to obtain the result of the computation. If however Thread A has a lot of work to do and the computation of the task takes a lot of time, this can be a problem.
Imagine the following example: you’re developing a game with the following participants: a game-client to visualize the game and read the user’s input, a server that validates and processes the client-data and a database that persists all relevant gamedata. Whenever a new gameobject (whatever that might be) is created by the gamelogic this object must be persisted into the database. To decouple gamelogic from database-operations you choose to submit all db-relevant tasks to an ExecutorService. Additionaly the communication between client and server is based on the id of the gameobjects which is assigned by the database.
- Client reacts on user’s input: Create new uber-soldier!
- Client sends request to server.
- Server (Thread A) validates request and creates a new task that is submitted to database-layer (Thread B).
- Thread A calls Future.get() to obtain the new uber-soldier-id. Thread A waits until Thread B is done (database assigned primary key).
- Server sends response to client with new uber-soldier-id.
Problems may arise when Thread A must handle messages from hundreds or thousands of clients. The longer the key-assignment by the database takes, the longer Thread A must wait. Sooner or later this leads into slow pings for every client. Players leave, your company is about to get broke. What to do?
One option is to assign the primary-keys not by database but by the game-server itself. However this gets complicated when several servers writing on several databases. Another option is a variant of the Observer-pattern. What if you could register Thread A as a listener on the future? Instead of blocking through a call on Future.get(), the listener would be notified when the key-assignment by the database is done. Great! Let me introduce the concept of NotifyingExecutorService.
While thinking about the problem, I found myself crawling through the internet and some libraries to make sure I won’t reinvent the wheel. Finally I stumbled upon the (awesome) guava-library (version 10+), and found their solution of the problem called ListeningExecutorService. That was almost all I was looking for: register to a (extended) Future and get notified when computation is done. Sadly the google-implementation only notifies the listener without passing the result to them. I did not know why they made this, but that is where I started coding.