The main function of Completion Service is to obtain the completed asynchronous tasks one by one according to the order of completion of the asynchronous tasks. The main implementation is in Executor Completion Service.
Class diagram
Core internal classes
private class QueueingFuture extends FutureTask<Void> { QueueingFuture(RunnableFuture<V> task) { super(task, null); this.task = task; } protected void done() { completionQueue.add(task); } private final Future<V> task; }
In the implementation of Completion Service, the task FutureTask is extended to implement the done method of FutureTask. When the task is completed, the method will be called back. Then we put the completed tasks in the queue in this method, and the results of the tasks will be processed one by one according to the order of the asynchronous tasks.
Core attributes
// Thread pool for task execution private final Executor executor; // Blocking queues for completed asynchronous tasks, using LinkedBlockingQueue by default private final BlockingQueue<Future<V>> completionQueue;
Constructor
public ExecutorCompletionService(Executor executor) { if (executor == null) throw new NullPointerException(); this.executor = executor; this.aes = (executor instanceof AbstractExecutorService) ? (AbstractExecutorService) executor : null; this.completionQueue = new LinkedBlockingQueue<Future<V>>(); } public ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue) { if (executor == null || completionQueue == null) throw new NullPointerException(); this.executor = executor; this.aes = (executor instanceof AbstractExecutorService) ? (AbstractExecutorService) executor : null; this.completionQueue = completionQueue; }
In the constructor, we need to pass in at least one implementation of Executor thread pool to perform the asynchronous task, but it is recommended to pass in another blocking queue. The default LinkedBlocking Queue is an unbounded queue with the risk of memory overflow.
Submit submit task
public Future<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); RunnableFuture<V> f = newTaskFor(task); executor.execute(new QueueingFuture(f)); return f; }
We can see that we encapsulate tasks as Queueing Future tasks before submitting them to the thread pool. When the task is completed, the execution done method is called back and the task is queued.
Obtain completed tasks
public Future<V> take() throws InterruptedException { return completionQueue.take(); } public Future<V> poll() { return completionQueue.poll(); }
- take: If there are no tasks, keep blocking until new tasks come in
- poll: Return to NULL without a task
Example
public class CompletionServiceTest { @Test public void test() throws ExecutionException, InterruptedException { Random random = new Random(); ExecutorService executor = Executors.newFixedThreadPool(10); CompletionService<Integer> completionService = new ExecutorCompletionService<>(executor, new LinkedBlockingQueue<>(10)); for (int i = 0; i < 8; i++) { completionService.submit(() -> { int time = random.nextInt(1000); sleep(time); System.out.println(Thread.currentThread().getName() + " Execution of asynchronous tasks is time-consuming: " + time); return time; }); } while (true) { System.out.println(Thread.currentThread().getName() + " The main thread gets the task result " + completionService.take().get()); } } public static void sleep(int probe) { try { Thread.sleep(probe); } catch (InterruptedException e) { e.printStackTrace(); } } }
pool-1-thread-7 takes time to execute asynchronous tasks: 153 Main main main thread gets task result 153 pool-1-thread-5 takes time to execute asynchronous tasks: 208 Main main main thread gets task result 208 pool-1-thread-4 takes time to execute asynchronous tasks: 242 Main main main thread gets task result 242 pool-1-thread-8 takes time to execute asynchronous tasks: 456 Main main main thread gets task result 456 pool-1-thread-1 takes time to execute asynchronous tasks: 567 Main main main thread gets task result 567 pool-1-thread-2 takes time to execute asynchronous tasks: 782 Main main main thread gets task result 782 pool-1-thread-6 takes time to execute asynchronous tasks: 796 Main main main thread gets task result 796 pool-1-thread-3 takes time to execute asynchronous tasks: 976 Main main main thread gets task result 976
Mine is an 8-core machine, so the end time of the task must be sorted according to the end time of the task.
Source code
https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases
spring-boot-student-concurrent project
layering-cache
Laying-cache, a multi-level caching framework for monitoring This is an implementation of my open source multi-level caching framework. If you are interested, take a look at it.