В предыдущих статьях о фреймворке java.util.concurrent я представил, что для меня является ядром пакета и что вы будете использовать чаще всего в реальных приложениях.
Если идти дальше с этой темой, то стоит знать о новой фреймворке fork / join, представленном в Java 7. Плюсы и минусы этой фреймворки по сравнению с использованием пулов потоков или обычной разработки потоков / выполнимых приложений были предметом многих дискуссий, но Я думаю, что это все еще хороший инструмент в вашем арсенале.
Я буду использовать более классический подход, чтобы показать вам базовую структуру структуры fork / join и использовать ее с помощью кода алгоритма Quicksort .
Моя цель — познакомить вас с инструментами, предоставляемыми структурой fork / join в Java 7, это не обсуждение лучшей реализации Quicksort. Если вы хотите вникнуть в лучшее из возможного Quicksort, я рекомендую вам прочитать этот замечательный пост об этом.
Среда fork / join предоставляет очень простую и интуитивно понятную структуру для реализации рекурсивных и разделяющих и побеждающих задач, которые могут быть решены одновременно. Вы начинаете с большой проблемы и разбиваете ее на более мелкие рабочие единицы, пока каждая рабочая единица не может быть решена напрямую.
Реализация вашего решения проблем должна быть подклассом ForkJoinTask , и вы, скорее всего, будете реализовывать подкласс его дочерних объектов RecursiveAction или RecursiveTask ; RecursiveAction представляет рекурсивное решение проблем, способное непосредственно решать достаточно малую проблему или разбивать ее на более мелкие рабочие блоки; RecursiveTask похож, но он также имеет возможность возвращать результат для каждого выполненного рабочего блока.
Для реализации нашей быстрой сортировки мы будем использовать стратегию на месте (мы хардкорные, верно?) И использовать общий список в качестве данных, чтобы он поддерживал любую Java Comparable. С этими предпосылками наша задача может быть закодирована как:
package com.ricardozuasti; import java.util.List; import java.util.concurrent.RecursiveAction; public class QuickSort<T extends Comparable> extends RecursiveAction { private List<T> data; private int left; private int right; public QuickSort(List<T> data){ this.data=data; this.left = 0; this.right = data.size() - 1; } public QuickSort(List<T> data, int left, int right){ this.data = data; this.left = left; this.right = right; } @Override protected void compute() { if (left < right){ int pivotIndex = left + ((right - left)/2); pivotIndex = partition(pivotIndex); invokeAll(new QuickSort(data, left, pivotIndex-1), new QuickSort(data, pivotIndex+1, right)); } } private int partition(int pivotIndex){ T pivotValue = data.get(pivotIndex); swap(pivotIndex, right); int storeIndex = left; for (int i=left; i<right; i++){ if (data.get(i).compareTo(pivotValue) < 0){ swap(i, storeIndex); storeIndex++; } } swap(storeIndex, right); return storeIndex; } private void swap(int i, int j){ if (i != j){ T iValue = data.get(i); data.set(i, data.get(j)); data.set(j, iValue); } } }
Метод compute () — это метод, который будет вызывать инфраструктура fork / join для каждого рабочего модуля. Наш базовый шаг (небольшой рабочий блок) — ничего не делать (так как список размера 1 всегда сортируется). Наш рекурсивный шаг использует метод invokeAll () , который является ярлыком для fork () всех небольших рабочих модулей, а затем снова присоединяет их к ним () .
Вот и все! Нам больше ничего не нужно. Чтобы фактически использовать наш новый параллельный Quicksort, мы используем экземпляр ForkJoinPool :
package com.ricardozuasti; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ForkJoinPool; public class Concurrency3 { public static void main(String[] args) { final int SIZE = 10000; List<Integer> myList = new ArrayList<Integer>(SIZE); for (int i=0; i<SIZE; i++){ int value = (int) (Math.random() * 100); myList.add(value); } QuickSort<Integer> quickSort = new QuickSort<Integer>(myList); ForkJoinPool pool = new ForkJoinPool(); pool.invoke(quickSort); } }
И снова метод invoke эквивалентен выполнению fork (), за которым следует join () , поэтому после возврата метода pool.invoke () наш список сортируется.