Статьи

Java 7: познакомьтесь с Fork / Join Framework

JSR-166 (y) — это официальное название этой новой функции, включенной в Java 7. Если вы заметили, что в имени есть буква «y», это происходит потому, что JSR-166 (Утилиты параллелизма) добавляются после Java 5 , но это не остановится, поскольку уже есть планы добавить новые классы в Java 8 под JSR-166 (e). Проверьте эту страницу, поддерживаемую Дугом Ли, создателем JSR-166, для получения дополнительной информации.

Согласно Википедии, параллелизм — это «одновременное выполнение некоторой комбинации нескольких экземпляров запрограммированных инструкций и данных на нескольких процессорах», а в Java есть классы и интерфейсы для достижения этого (вроде…) начиная с Дня 1. Вы можете знать их как: java .lang.Thread , java.lang.Runnable и т. д. То, что делают утилиты параллелизма (пакет java.util.concurrent ), упрощает кодирование параллельных задач, поэтому наш код намного проще и чище. Как разработчики, мы не должны были ничего делать при запуске наших приложений на машинах с более высокими ресурсами обработки, очевидно, производительность наших приложений улучшится, но действительно ли мы используем ресурсы обработки по максимуму? Ответ большой НЕТ.

Этот пост покажет вам, как среда Fork / Join поможет нам максимально использовать ресурсы обработки при работе с проблемами, которые можно разделить на небольшие проблемы, и все решения каждой из этих небольших проблем приводят к решению больших проблем. проблема (например, рекурсия, разделяй и властвуй).

Что вам нужно

NetBeans 7+ или любая другая IDE, поддерживающая Java 7 JDK 7+
Размытие на изображении , пример из Oracle

Основы

Инфраструктура Fork / Join фокусируется на использовании всех ресурсов обработки, доступных на машине, для повышения производительности приложений. Он был разработан для упрощения параллелизма в алгоритмах «разделяй и властвуй» . Магия фреймворка Fork / Join заключается в его алгоритме кражи работы, в котором рабочие потоки, которые являются бесплатными, крадут задачи у других занятых потоков, поэтому все потоки работают постоянно. Ниже приведены основы, которые вы должны знать, чтобы начать использовать фреймворк:

  • Под вилкой подразумевается разбиение задачи на подзадачи и работа над ними.
  • Присоединение означает объединение решения каждой подзадачи в одно общее решение.
  • java.lang.Runtime использует этот класс для получения числа процессоров, доступных для виртуальной машины Java. Используйте метод + availableProcessors (): int для этого.
  • java.util.concurrent.ForkJoinPool Главный класс фреймворка, тот, который реализует алгоритм кражи работы и отвечает за выполнение задач.
  • java.util.concurrent.ForkJoinTask Абстрактный класс для задач, которые выполняются в java.util.concurrent.ForkJoinPool. Понимать задачу как часть всей работы, например, если вам нужно что-то сделать с массивом, одна задача может работать на позициях от 0 до n / 2, а другая задача — на позициях (n / 2) +1 в n-1 , где n — длина массива.
    • java.util.concurrent.RecursiveAction Подкласс абстрактного класса задачи, используйте его, когда задача не нужна для возврата результата, например, когда задача работает с позициями массива, она ничего не возвращает, потому что работал на массиве. Метод, который вы должны реализовать для выполнения работы, это compute (): void , обратите внимание на возврат void.
    • java.util.concurrent.RecursiveTask Подкласс абстрактного класса задач, используйте его, когда ваши задачи возвращают результат. Например, при вычислении чисел Фибоначчи каждая задача должна возвращать вычисленное ею число, чтобы объединить их и получить общее решение. Метод, который вы должны реализовать для выполнения работы, это compute (): V , где V — тип возврата; для примера Фибоначчи V может быть java.lang.Integer.

При использовании фреймворка вы должны определить флаг, который указывает, нужно ли форк / присоединение к задачам или нужно ли вычислять работу напрямую. Например, при работе с массивом вы можете указать, что если длина массива превышает 500_000_000, вы должны выполнить / объединить задачи, в противном случае массив будет достаточно маленьким для непосредственного вычисления. По сути, алгоритм, которому вы должны следовать, показан ниже:

1
2
3
4
5
6
7
8
9
if(the job is small enough)
{
   compute directly
}
else
{
   split the work in two pieces (fork)
   invoke the pieces and join the results (join)
}

Хорошо, сейчас слишком много теории, давайте рассмотрим пример.

Пример

Размытие изображения требует работы на каждом пикселе изображения. Если изображение достаточно большое, у нас будет большой массив пикселей для работы, поэтому мы можем использовать fork / join для работы с ними и максимально использовать ресурсы обработки. Вы можете скачать исходный код с сайта Java ™ Tutorials .

Загрузив исходный код, откройте среду IDE NetBeans 7.x и создайте новый проект:

Затем выберите « Проект Java с существующими источниками» из категории «Java» во всплывающем окне:

Выберите имя и папку проекта и нажмите Далее>

Теперь выберите папку, в которую вы скачали исходный код для Blur на примере изображения :

И выберите файл ForkBlur.java, затем нажмите «Готово»:

Исходный код будет импортирован, и будет создан новый проект. Обратите внимание, что новый проект показан с ошибками, это потому, что Java 7 не включен по умолчанию:

Чтобы это исправить, щелкните правой кнопкой мыши на имени проекта и выберите опцию « Свойства» . Во всплывающем диалоговом окне перейдите в Библиотеки и выберите JDK 1.7 из ComboBox платформы Java :

Теперь перейдите к опции Sources и выберите JDK 7 в комбинированном списке Source / Binary Format :

И последнее, но не менее важное: увеличьте объем памяти, выделенной виртуальной машине, при запуске этого приложения, поскольку мы будем обращаться к массиву из 5 миллионов позиций (или более). Перейдите к параметру Выполнить и вставьте -Xms1024m -Xmx1024m в TextBox параметров виртуальной машины :

Нажмите OK, и ваш проект должен быть собран без ошибок. Теперь нам нужно найти изображение, достаточно большое, чтобы у нас был большой массив для работы. Через некоторое время я нашел несколько замечательных изображений (около 150 МБ) с планеты Марс, благодаря роботу любопытства вы можете загрузить свои отсюда . Как только вы загрузите изображение, вставьте его в папку проекта.

Перед запуском примера нам нужно изменить исходный код, чтобы контролировать, когда его запускать с использованием инфраструктуры Fork / Join. В файле ForkBlur.java перейдите к строке 104, чтобы изменить имя изображения, которое мы будем использовать:

1
2
3
//Change for the name of the image you pasted
//on the project's folder.
String filename = 'red-tulips.jpg';

Затем замените строки от 130 до 136 следующим фрагментом кода:

01
02
03
04
05
06
07
08
09
10
11
ForkBlur fb = new ForkBlur(src, 0, src.length, dst);
        boolean computeDirectly = true;
 
        long startTime = System.currentTimeMillis();
        if (computeDirectly) {
            fb.computeDirectly();
        } else {
            ForkJoinPool pool = new ForkJoinPool();
            pool.invoke(fb);
        }
        long endTime = System.currentTimeMillis();

Обратите внимание на флаг computeDirectly . Когда это правда , мы НЕ будем использовать fork / Join Framework, вместо этого мы будем вычислять задачу напрямую. При значении false будет использоваться структура fork / join.

Метод compute (): void в классе ForkBlur реализует алгоритм fork / join. Он основан на длине массива, когда длина массива больше 10_000, задача будет разветвлена, в противном случае задача будет вычислена напрямую.

После того, как вы можете увидеть мои 2 процессора при выполнении Blur на примере изображения без использования инфраструктуры Fork / Join ( computeDirectly = true ), потребовалось около 14 секунд для завершения работы:

Вы можете видеть, что процессоры работают, но не по максимуму. При использовании инфраструктуры Fork / Join ( computeDirectly = false ) вы можете видеть, что они работают на 100%, и на завершение работы ушло почти на 50% меньше времени:

Это видео показывает полный процесс:

Я надеюсь, что вы видите, насколько полезен этот фреймворк. Конечно, вы не можете использовать все это в своем коде, но когда у вас есть задача, которая может быть разделена на маленькие задачи, вы знаете, к кому обращаться.

Ссылка: Java 7: познакомьтесь с Fork / Join Framework от нашего партнера JCG Алексиса Лопеса в блоге Java и ME .