CompletionService in Java Concurrent Programming

Keywords: Java Junit

Recently, I met a requirement, one of which is time-consuming. In order to improve efficiency, I choose to start a new thread asynchronously. Due to the need to operate on the return value of the method, I choose to use FutureTask, which can get the task circularly and call the get method to get the execution result of the task. However, if the task is not completed, the thread to get the result will block until the task is completed. Later, the CompletionService class was discovered, which integrates the functions of Executor and BlockingQueue. You can submit the Callable task to it for execution, and then use the take method similar to the queue to get the return value of the thread.

The demo is as follows:

package com.aliyun.callback;

import org.junit.Test;

import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by Demon, on 2018/4/25
 */
public class CompletionServiceTest {

    private static final int DEFAULT_CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() <= 1 ? 2 :
            Runtime.getRuntime().availableProcessors() * 2;

    private static final int DEFAULT_MAXNUM_POOL_SIZE = 32;

    private static final int DEFAULT_KEEP_ALIVE_TIME = 30;

    private  ThreadPoolExecutor threadPool = new ThreadPoolExecutor(DEFAULT_CORE_POOL_SIZE, DEFAULT_MAXNUM_POOL_SIZE
            , DEFAULT_KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(20));

    @Test
    public void test() {
        CompletionService<String> completionService = new ExecutorCompletionService<>(threadPool);
        try {
            for (int i = 0; i < 5; i++) {
                Task task = new Task("threadName" + i);
                // Using submit main thread can catch exceptions in asynchronous thread
                completionService.submit(task);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 5; i++) {
            try {
                Future<String> future = completionService.take();
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    private class Task implements Callable<String> {

        private String name;

        private Task(String name) {
            this.name = name;
        }

        @Override
        public String call() throws Exception {
            int sleepTime = new Random().nextInt(1000);
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Value returned to the caller
            String str = name + " sleep time:" + sleepTime;
            System.out.println(name + " finished...");
            // throw new RuntimeException("test catch error");
            return str;
        }
    }
}
//Operation result:
threadName0 finished...
threadName0 sleep time:140
threadName4 finished...
threadName4 sleep time:250
threadName3 finished...
threadName3 sleep time:258
threadName1 finished...
threadName1 sleep time:416
threadName2 finished...
threadName2 sleep time:712

When using CompletionService to maintain the return results of processing threads, the main thread can always get the return value of the first completed task, regardless of the order in which they are added to the thread pool, which can reduce the blocking time of the main thread and is better than the native future.get method.

Posted by barbs75 on Sun, 22 Mar 2020 07:44:28 -0700