разделяй и властвуй в реальной жизни
Идея разделяй и властвуй, как следует из названияразделяй и властвуй.就像古代的王想治理好天下,单单靠他一个人是不够的,还需要大臣的辅助,把天下划分为一块块区域,分派的下面的人负责,然后下面的人又分派给他们的属下负责,层层传递。
Это разделяй и властвуй, то есть сложная проблема разлагается на похожие подзадачи, а потом подзадачи есть подзадачи, пока проблема не станет совсем простой и ее уже не нужно будет делить. Затем результаты задачи возвращаются слой за слоем и, наконец, докладываются королю!Разделяй и властвуй имеет множество приложений в алгоритмах, подобных MapReduce для больших данных,алгоритм слияния, алгоритм быстрой сортировки и т. д.
Платформа параллельных вычислений под названием Fork/Join также предоставляется в JUC для обработки ситуации «разделяй и властвуй», которая аналогична автономной версии.MapReduce
.
Fork/Join
Разделяй и властвуй разделен на два этапа,Первый этап разбивает задачу, разбивает задачу на маленькие задачи до тех пор, пока маленькие задачи нельзя будет просто вычислить и вернуть.
На втором этапе результаты объединяются, а результаты каждой небольшой задачи объединяются и возвращаются для получения конечного результата.. а такжеFork
декомпозировать задачи,Join
является объединенным результатом.
Фреймворк Fork/Join в основном состоит из двух частей:ForkJoinPool、ForkJoinTask
.
ForkJoinPool
Задача управления состоит в том, чтобы разделить и завоевать пул потоков. Это упоминалось в предыдущей статьеThreadPoolExecutor
Пул потоков, общим моментом является реализация шаблона потребитель-производитель, но есть и некоторые отличия.ThreadPoolExecuto
Пул потоков R имеет только одну очередь задач, иForkJoinPool
Есть несколько очередей задач. пройти черезForkJoinPool
изinvoke
илиsubmit
илиexecute
При отправке задачи она будет распределяться по разным очередям задач в соответствии с определенными правилами, а также в двухстороннюю очередь очереди задач.
выполняться асинхронно, результат не возвращается вызвать синхронизацию, есть возвращаемый результат (будет блокироваться) submit является асинхронным, с возвращаемым результатом (будущее)
Почему дека? потому чтоForkJoinPool
Есть механизм,Когда очередь задач, соответствующая потреблению рабочим потоком, простаивает, она переходит в хвост других занятых очередей задач для выполнения задач кражи (хороший партнер).. Затем занятая очередь задач по-прежнему выводит задачу в соответствующий рабочий поток для использования. Таким образом, два конца находятся в упорядоченном порядке, и не будет конкуренции за задачи.
ForkJoinTask
Это задача разделяй и властвуй, что эквивалентно тому, что мы используем в будние дни.Runnable
. Это абстрактный класс, основной методfork
а такжеjoin
.fork
метод используется для асинхронного выполнения подзадачи,join
Метод блокирует текущий поток и ожидает возврата подзадачи.
ForkJoinTask
Есть два подкласса, которыеRecursiveAction
а такжеRecursiveTask
. Эти два подкласса также являются абстрактными классами, и оба рекурсивно выполняют задачи «разделяй и властвуй». Каждый подкласс имеет абстрактные методыcompute
Разница в том,RecursiveAction
не возвращаемое значение иRecursiveTask
Имеет возвращаемое значение.
Простое приложение
Классический случай рекурсивной реализации идеи «разделяй и властвуй» — это последовательность Фибоначчи.
Последовательность Фибоначчи: 1, 1, 2, 3, 5, 8, 13, 21, 34, … Формула: F(1)=1, F(2)=1, F(n)=F(n-1)+F(n-2) (n>=3, n∈N*)
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool(4); // 最大并发数4
Fibonacci fibonacci = new Fibonacci(20);
long startTime = System.currentTimeMillis();
Integer result = forkJoinPool.invoke(fibonacci);
long endTime = System.currentTimeMillis();
System.out.println("Fork/join sum: " + result + " in " + (endTime - startTime) + " ms.");
}
//以下为官方API文档示例
static class Fibonacci extends RecursiveTask<Integer> {
final int n;
Fibonacci(int n) {
this.n = n;
}
@Override
protected Integer compute() {
if (n <= 1) {
return n;
}
Fibonacci f1 = new Fibonacci(n - 1);
f1.fork();
Fibonacci f2 = new Fibonacci(n - 2);
return f2.compute() + f1.join();
}
}
Конечно, вы можете сделать и то, и другоеfork
, обратите внимание, что обе задачиfork
случае необходимо соблюдатьf1.fork(),f2.fork(), f2.join(),f1.join()
Такой порядок, иначе будут проблемы с производительностью. В официальной документации JDK есть инструкции, и те, кому это интересно, могут их изучить.
Я рекомендую использовать метод invokeAll
Fibonacci f1 = new Fibonacci(n - 1);
Fibonacci f2 = new Fibonacci(n - 2);
invokeAll(f1,f2);
return f2.join() + f1.join();
Method invokeAll (available in multiple versions) performs the most common form of parallel invocation: forking a set of tasks and joining them all.
Официальный документ API написан так, поэтому обычно используетсяinvokeAll
Достаточно. invokeAll передаст первую входящую задачу текущему потоку для выполнения, а все остальные задачи будут разветвлены и добавлены в рабочую очередь, что означает, что текущий поток также используется для выполнения задачи. Ниже приведеныinvokeAll
исходный код
public static void invokeAll(ForkJoinTask<?>... tasks) {
Throwable ex = null;
int last = tasks.length - 1;
for (int i = last; i >= 0; --i) {
ForkJoinTask<?> t = tasks[i];
if (t == null) {
if (ex == null)
ex = new NullPointerException();
}
else if (i != 0) //除了第一个都fork
t.fork();
else if (t.doInvoke() < NORMAL && ex == null) //留一个自己执行
ex = t.getException();
}
for (int i = 1; i <= last; ++i) {
ForkJoinTask<?> t = tasks[i];
if (t != null) {
if (ex != null)
t.cancel(false);
else if (t.doJoin() < NORMAL)
ex = t.getException();
}
}
if (ex != null)
rethrow(ex);
}
Эпилог
Fork/Join
Это фреймворк, использующий идею «разделяй и властвуй», и многие сцены в будние дни могут использовать идею «разделяй и властвуй». ядро фреймворкаForkJoinPool
, из-за особенностей очереди задач и кражи он может лучше использовать ресурсы.
Наконец, я желаю вам всем счастливой пятницы!
Если есть ошибки, поправьте меня! Личный публичный аккаунт: стратегия прокачки да
Есть соответствующие дополнительные материалы для интервью, ожидающие сбора