Пример Java Callable Future

Java Callable и Future часто используются в многопоточном программировании. В последних нескольких сообщениях мы много узнали о потоках Java, но иногда нам бы хотелось, чтобы поток мог вернуть какое-то значение, которое мы могли бы использовать. В Java 5 был введен интерфейс java.util.concurrent.Callable в пакете concurrency, который аналогичен интерфейсу Runnable, но может возвращать любой объект и бросать исключение.

Java Callable

Интерфейс Java Callable использует обобщения для определения типа возвращаемого объекта. Класс Executors предоставляет полезные методы для выполнения Java Callable в пуле потоков. Поскольку вызываемые задачи выполняются параллельно, мы должны ждать возвращаемого объекта.

Java Future

Задачи Java Callable возвращают объект java.util.concurrent.Future. Используя объект Java Future, мы можем узнать статус задачи Callable и получить возвращаемый объект. Он предоставляет метод get(), который может ожидать завершения Callable и затем возвращать результат. Java Future предоставляет метод cancel() для отмены связанной задачи Callable. Существует перегруженная версия метода get(), где мы можем указать время ожидания результата, это полезно, чтобы избежать блокировки текущего потока на длительное время. Есть методы isDone() и isCancelled() для определения текущего статуса связанной задачи Callable. Вот простой пример задачи Java Callable, которая возвращает имя потока, выполняющего задачу, после одной секунды. Мы используем фреймворк Executor для параллельного выполнения 100 задач и используем Java Future для получения результата отправленных задач.

package com.journaldev.threads;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MyCallable implements Callable {

    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        //вернуть имя потока, выполняющего этот вызываемый задачи
        return Thread.currentThread().getName();
    }
    
    public static void main(String args[]){
        //Получить ExecutorService из утилитарного класса Executors, размер пула потоков - 10
        ExecutorService executor = Executors.newFixedThreadPool(10);
        //создать список для хранения объектов Future, связанных с Callable
        List> list = new ArrayList>();
        //Создать экземпляр MyCallable
        Callable callable = new MyCallable();
        for(int i=0; i< 100; i++){
            //представить Callable задачи для выполнения пулом потоков
            Future future = executor.submit(callable);
            //добавить Future в список, мы можем получить возвращаемое значение, используя Future
            list.add(future);
        }
        for(Future fut : list){
            try {
                //вывести возвращаемое значение Future, обратите внимание на задержку вывода в консоль
                //потому что метод Future.get() ждет завершения задачи
                System.out.println(new Date()+ "::"+fut.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        //выключить службу исполнителя сейчас
        executor.shutdown();
    }

}

После выполнения вышеуказанной программы вы заметите задержку вывода, потому что метод java Future get() ожидает завершения задачи Java callable. Также обратите внимание, что выполняются только 10 потоков для выполнения этих задач. Вот отрывок из вывода программы выше.

Mon Dec 31 20:40:15 PST 2012::pool-1-thread-1
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-3
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-4
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-5
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-6
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-7
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-8
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-9
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-10
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
...

Совет: А что, если мы хотим переопределить некоторые методы интерфейса Java Future, например, переопределить метод get(), чтобы установить время ожидания по умолчанию, а не ждать бесконечно, в этом случае пригодится класс Java FutureTask, который является базовой реализацией интерфейса Future. Посмотрите пример Java FutureTask Example, чтобы узнать больше об этом классе.

Source:
https://www.digitalocean.com/community/tutorials/java-callable-future-example