[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-mnogopotochnost-chto-takoe-freymvork-fork-join":3},{"id":4,"slug":5,"topicId":6,"topicSlug":7,"topicName":8,"topicEmoji":9,"question":10,"answer":11,"codeLang":12,"codeSrc":12,"important":12,"commonMistakes":12,"modernUsage":12,"difficulty":13,"tags":14,"related":20,"progress":21,"seo":22},278,"chto-takoe-freymvork-fork-join",8,"mnogopotochnost","Многопоточность","🔀","Что такое фреймворк Fork\u002FJoin?","\u003C!-- grade: 4\u002F5 — хорошее объяснение, добавлены пример с кодом, work-stealing, таблица сравнения -->\n\n**Fork\u002FJoin** — это фреймворк из пакета `java.util.concurrent`, появившийся в JDK 7 (JEP 166). Он предназначен для эффективного выполнения задач, которые можно **рекурсивно разбить на независимые подзадачи** и затем объединить результаты.\n\n**Два этапа:**\n\n1. **Fork (разделение)** — большая задача разделяется на подзадачи, каждая из которых может быть разделена далее, пока задача не станет достаточно маленькой для последовательного решения.\n2. **Join (объединение)** — результаты подзадач собираются (сворачиваются) обратно в результат исходной задачи.\n\n**Ключевые классы:**\n\n| Класс | Назначение |\n|---|---|\n| `ForkJoinPool` | Пул потоков, исполняющий `ForkJoinTask` |\n| `ForkJoinTask\u003CV>` | Абстрактная задача для Fork\u002FJoin |\n| `RecursiveTask\u003CV>` | Задача, возвращающая результат |\n| `RecursiveAction` | Задача без возвращаемого значения |\n\n**Пример — параллельная сумма массива:**\n\n\u003Cdetails>\n\u003Csummary>Код: параллельная сумма через RecursiveTask\u003C\u002Fsummary>\n\n```java\nclass SumTask extends RecursiveTask\u003CLong> {\n    private static final int THRESHOLD = 10_000;\n    private final long[] array;\n    private final int from, to;\n\n    SumTask(long[] array, int from, int to) {\n        this.array = array;\n        this.from = from;\n        this.to = to;\n    }\n\n    @Override\n    protected Long compute() {\n        int length = to - from;\n        if (length \u003C= THRESHOLD) {\n            \u002F\u002F Достаточно маленькая задача — считаем последовательно\n            long sum = 0;\n            for (int i = from; i \u003C to; i++) {\n                sum += array[i];\n            }\n            return sum;\n        }\n        \u002F\u002F Разделяем на две подзадачи\n        int mid = from + length \u002F 2;\n        SumTask left = new SumTask(array, from, mid);\n        SumTask right = new SumTask(array, mid, to);\n\n        left.fork();  \u002F\u002F Отправляем левую подзадачу в пул\n        long rightResult = right.compute(); \u002F\u002F Правую считаем в текущем потоке\n        long leftResult = left.join();      \u002F\u002F Ждём результат левой\n\n        return leftResult + rightResult;\n    }\n}\n\n\u002F\u002F Использование\nlong[] data = new long[1_000_000];\nArrays.fill(data, 1L);\n\nForkJoinPool pool = new ForkJoinPool(); \u002F\u002F или ForkJoinPool.commonPool()\nlong sum = pool.invoke(new SumTask(data, 0, data.length));\nSystem.out.println(\"Сумма: \" + sum); \u002F\u002F 1000000\n```\n\n\u003C\u002Fdetails>\n\n**Алгоритм work-stealing.** Каждый поток в `ForkJoinPool` имеет собственную двустороннюю очередь задач (deque). Когда поток завершает свои задачи, он **«крадёт» задачи** из конца очереди другого потока. Это обеспечивает автоматическую балансировку нагрузки: ни один поток не простаивает, пока есть незавершённые задачи.\n\n**`ForkJoinPool.commonPool()`** — общий пул на всё приложение. Используется по умолчанию для параллельных стримов (`Stream.parallel()`), `CompletableFuture.supplyAsync()` и других операций.\n\n> Для решения некоторых задач этап Join не требуется. Например, для параллельного QuickSort — массив рекурсивно делится на всё меньшие и меньшие диапазоны, пока не вырождается в тривиальный случай из 1 элемента. Хотя в некотором смысле Join будет необходим и тут, т.к. всё равно остаётся необходимость дождаться, пока не закончится выполнение всех подзадач.\n\n> **Аналогия:** Fork\u002FJoin — это организация работы в большом ресторане. Шеф-повар (главная задача) распределяет работу по поварам (подзадачи): один готовит соус, другой — мясо, третий — гарнир. Если повар закончил раньше, он помогает другому (work-stealing). Когда все закончили, блюдо собирается (join).\n\n> **На собеседовании** могут спросить: «Почему важно вызывать `fork()` для одной подзадачи, а `compute()` для другой, а не `fork()` для обеих?» Ответ: если сделать `fork()` для обеих, текущий поток освободится и будет ждать — это расточительно. Вызывая `compute()` для одной из подзадач, мы загружаем текущий поток полезной работой.","","middle",[15,16,17,18,19],"work-stealing","параллелизм","Fork\u002FJoin","ForkJoinPool","concurrency",[],null,{"title":23,"description":24,"ogTitle":25,"ogDescription":26,"keywords":27,"schemaAnswer":32,"featuredSnippetReady":33},"Фреймворк Fork\u002FJoin в Java — рекурсивное распараллеливание задач — Gymterview","Fork\u002FJoin (JDK 7) рекурсивно разбивает задачу на подзадачи для параллельного выполнения. Использует work-stealing алгоритм для балансировки нагрузки.","Fork\u002FJoin фреймворк в Java — как работает","Fork\u002FJoin разбивает задачу на подзадачи рекурсивно, решает параллельно и объединяет результат. Использует work-stealing для эффективности.",[28,18,29,30,31],"Fork Join Java","work-stealing алгоритм","параллельные вычисления Java","RecursiveTask","Фреймворк Fork\u002FJoin (JDK 7) — набор классов для параллельного выполнения задач, которые можно рекурсивно разбить на подзадачи. На этапе Fork задача делится на меньшие, на этапе Join решения объединяются. Использует work-stealing алгоритм: потоки, завершившие свои подзадачи, могут «украсть» подзадачи у занятых потоков.",true]