Completion Service Source Parsing

Posted by Ashoar on Wed, 25 Sep 2019 07:04:36 +0200

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.

Topics: Programming Spring github