The package is a ton of utilities for developing concurrent programs in Java: concurrent maps, synchronization strategies, blocking queues, thread management, and many more. The latter is the one we are concerned about: thread management. Thread management in this package all starts with the Executors helper class and the ExecutorService interface. The Executors helper class provides easy methods to create an ExecutorService as either a cached thread pool that grows on demand as new threads are needed or a fixed thread pool that ensures tasks are queued once the threads are exhausted. As a developer all you need to do is simply submit new tasks and the task will be executed in the background once a thread is available. The result is a Future . With the future in hand, you can poll for the completion of the event or simply wait for it. With only a few lines of code, we have created a re-usable and thread managed system for querying APIs.
import java.util.concurrent.*;
public class ApiService {
private final int THREADS = Runtime.getRuntime().availableProcessors();
private ExecutorService executor = Executors.newFixedThreadPool(THREADS);
public void invokeApi(String path) {
Future<Object> future = executor.submit(new Callable<Object>() {
public Object call() {
// query the API (via HttpURLConnection, HTTP Client, commons http, etc)
// map the API to POJOs (jaxb, JSON mappers, etc)
return result;
}
});
Object result = future.get(5, TimeUnit.SECONDS)
// do stuff with result
}
}
With these few lines, we ensure that only THREADS count of tasks will run concurrently with very few lines of code. Before the addition of these packages and classes, this type of programming required several libraries and classes.
With Groovy, this becomes even simpler as you can directly use closures without the new Callable() { public void call() { } } boilerplate. JDK 7 and Lambda also will remove the boilerplate code.
Now that we have this code in place we can do a few things to improve response times and processing.
First, if you only need one API invocation per request, the only thing this snippet provides is thread queuing. If you have multiple invocations, then you can submit all of the independent ones and wait on them so they invoke concurrently rather than serially. Rather than 3x time, it is just 1x.
Second, if you are using the Servlet 3.0 API, you can pause a request while waiting on the API invocation to complete to allow the servlet container to use the request thread to process other incoming threads. This will provide greater request thread usage and higher throughput of the entire system. I would not recommend this if the expected API processing times and latency are below 250-500ms as the context switching between threads could become expensive if the servlet container is pausing, processing, unpausing, etc threads over and over.