The ExecutorService is adding a latency, because it has to manage the worker threads and work queue.
But this should NOT be the reason for the 12ms difference.
As far as I can see, there are several issues here:
- maaartinus mentioned that you are "saturating" your pool. But for me, it seems like the opposite: Each task is submitted, and the submitting thread is waiting (via future.get) until the task is finished
- The resolution of your timer may be too low to measure <10ms time spans reliably (this depends of several factors - especially Windows systems are said to have a comparatively low timer resolution)
- In one case, you are measuring the time for
System.out.println(response)
, and in the other case, you are not
EDIT: Upon request in the comments, an example of the ExecutorCompletionService:
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ExecutorCompletionExample
{
public static void main(String[] args)
{
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletionService<String> completionService =
new ExecutorCompletionService<String>(executor);
int n = 500;
for (int i = 0; i < n; i++)
{
Task task = new Task("Task"+i);
completionService.submit(task);
}
for (int i = 0; i < n; ++i)
{
try
{
Future<String> future = completionService.take();
String response = future.get();
System.out.println("Response: "+response);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
return;
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
}
}
class Task implements Callable<String>
{
private static Random random = new Random(0);
private String name;
private long delay;
Task(String name)
{
this.name = name;
this.delay = 10 + random.nextInt(10)*10;
}
public String call() throws Exception
{
//String response = restTemplate.getForObject(url, String.class);
Thread.sleep(delay);
String response = "Response of "+name+" with delay "+delay;
return response;
}
}
The usage is rather simple, and to my understanding, the original question referred to a task that corresponds to the description of the CompletionService
interface:
"A service that decouples the production of new asynchronous tasks from the consumption of the results of completed tasks. .. A
CompletionService
can for example be used to manage asynchronous IO, in which tasks that perform reads are submitted in one part of a program or system, and then acted upon in a different part of the program when the reads complete, possibly in a different order than they were requested."
Whether or not this really matches the intended use-case (and, of course, whether it helps to achieve the desired reduction of overall latency) could not be tested based on the given example.