Интерфейс Runnable инкапсулирует задачу, выполняющуюся асинхронно. Вы можете воспринимать это как асинхронный метод без параметров и возвращаемого значения. Callable подобен Runnable, но с возвратом значения. Интерфейс Callable является параметризованным типом, с единственным общедоступным методом call().
1 2 3 4 |
public interface Callable; { V call() throws Exception; } |
Параметр представляет собой тип возвращаемого значения. Например, Callable представляет асинхронное вычисление, которое в конечном итоге возвращает объект Integer.
Future хранит результат асинхронного вычисления. Вы можете запустить вычисление, предоставив кому-либо объект Future, и забыть о нем. Владелец объекта Future может получить результат, когда он будет готов.
Интерфейс Future имеет следующие методы.
1 2 3 4 5 6 7 |
public interface Future { V get() throws . . .; V get(long timeout, TimeUnit unit) throws . . .; boolean isCancelled(); boolean isDone(); } |
Вызов первого метода get() устанавливает блокировку до тех пор, пока не завершится вычисление. Второй метод генерирует исключение TimeoutException, если истекает таймаут до завершения вычислений. Если прерывается поток, выполняющий вычисление, оба метода генерируют исключение InterruptedException. Если вычисление уже завершено, get() немедленно возвращает управление.
Метод isDone() возвращает false, если вычисление продолжается, и true — если оно завершено.
Вы можете прервать вычисление,вызвав метод Cancel(). Если вычисление еще не стартовало, оно отменяется и уже не будет запущено. Если же вычисление уже идет, оно прерывается в случае равенства true параметра mayInterrupt.
Любите задавать вопросы в Аске, но Вам не хватает подписчиков и лайков — попробуйте заказать их на сервисе avi1.ru. Здесь Вы получите данные ресурсы по очень дешевой стоимости, а также с высококачественным исполнением от лучших сотрудников техподдержки.
Класс-оболочка FutureTask представляет собой удобный механизм для превращения Callable одновременно в Future и Runnable, реализуя оба интерфейса.
Например:
1 2 3 4 5 6 |
Callable myComputation = . . .; FutureTask task = new FutureTask(myComputation); Thread t = ne Thread(task); // это Runnable t.start(); ... Integer result = task.get(); // это Future |
Программу которую вы увидите ниже демонстрирует эту концепцию в действии. Мы просто будем подсчитывать количество файлов, соответствующих критерию поиска. Таким образом, у нас будет долго работающая задача, которая в результате даст целочисленное значение — пример Callable.
1 2 3 4 |
class MatchCounter implements Callable{ public MatchCounter(File directory, String keyword) { . . . } public Integer call() { . . . } // возвращает количество найденных файлов } |
Сконструируем объект из MatchCounter и используем его для запуска потока:
1 2 3 |
FutureTask(counter); Thread t = new Thread(task); t.start(); |
И, наконец, напечатаем результат:
1 |
System.out.println(task.get() + " файлов найдено."); |
Конечно, вызов get() устанавливает блокировку до тех пор, пока не будет готов результат.
Внутри метода call() мы используем тот же механизм рекурсивно. Для каждого подкаталога создадим новый MathCounter и запустим поток для него. Также мы спрячем объект FutureTask в ArrayList<Future>. И в конце сложим все результаты:
1 2 |
for (Future result : results) count += result.get(); |
Каждый вызов get() устанавливает блокировку до тех пор, пока не будет готов результат. Конечно, потоки работают параллельно, так что есть шанс, что результаты будут готовы почти одновременно.
Вот полный код нашей программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
import java.io.*; import java.util.*; import java.util.concurrent.*; import javax.imageio.stream.FileImageInputStream; public class FutureTest { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.print("Укажите базовый каталог (например, /usr/loacl/jdk/lib): "); String directory = in.nextLine(); System.out.print("Введите ключевое слово (например, volatile): "); String keyword = in.nextLine(); MatchCounter counter = new MatchCounter(new File(directory), keyword); FutureTask task = new FutureTask(counter); Thread t = new Thread(task); t.start(); try { System.out.println(task.get() + " файлов найдено."); } catch(ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { } } } /** * Задача подсчитыает файлы в каталоге и всех его подкаталогах, * которые содержат указанное ключевое слово. */ class MatchCounter implements Callable { /** * Конструктор MatchCounter * @param directory Каталог, с которого начинается поиск * @param keyword Искомое ключевое слово */ public MatchCounter(File directory, String keyword) { this.directory = directory; this.keyword = keyword; } public Integer call() { count = 0; try { File[] files = directory.listFiles(); ArrayList results = new ArrayList(); for(File file : files) if(file.isDirectory()) { MatchCounter counter = new MatchCounter(file, keyword); FutureTask task = new FutureTask(counter); results.add(task); Thread t = new Thread(task); t.start(); } else { if(search(file)) count++; } for(Future result : results) try { count+= result.get(); } catch (ExecutionException e) { e.printStackTrace(); } } catch(InterruptedException e) { } return count; } /** * Ищем в файле заданное ключевое слово. * @param file Файл, в котором идет поиск * return true, если слово найдено в файле */ public boolean search(File file) { try { Scanner in = new Scanner((Readable) new FileImageInputStream(file)); boolean found = false; while(!found && in.hasNextLine()) { String line = in.nextLine(); if(line.contains(keyword)) found = true; } in.close(); return found; } catch (IOException e) { return false; } } private File directory; private String keyword; private int count; } |