Бесплатный курс по Java: от основ до продвинутого уровня
Fork/Join framework в Java
fork/join framework – для разбиения задачи на подзадачи и чтобы эти подзадачи выполнялись в отдельных потоках.
Также эти подзадачи могут тоже делиться на подзадачи и опять же чтобы эти подзадачи выполнялись в отдельных потоках.
Повторяться этот процесс деления может пока подзадача не станет необходимо мала.
Есть пул потоков. В один из его потоков ставиться большая задача.
Эта задача и подзадачи в будущем являют собой выполнения метода compile, который нужно переопределить
Переопределяется он всегда похожим образом:
ifзадача или подзадача уже необходимо мала, возвращаем что-то из метода compile.
else – делим задачу или подзадачу на подзадачи.
Деление на подзадачи в else происходит особым рекурсивным образом с помощью методов fork и join.
fork добавляет подзадачу в пул потоков и любой свободный поток может ее подхватить для выполнения. fork вызывает метод compile подзадачи, к которой fork был применен.
join ждет пока эта форкнутая подзадачавыполнится.
Внимание джойном останавливается выполнение кода, но поток не блокируется, поток может выполнять другую подзадачу из пула потоков.
join возвращает значение из compile, который fork вызвал.
Ясное дело, compile будет вызываться рекурсивно форком и соответственно джойны будут копитьсяпока не дойдет до наименьшей подзадачи.
И джойны, понятное дело, в обратном направлении начнут освобождаться.
Помещенные задачи форком в любом потоке в пул потоков могут быть взяты любым свободным потоком для выполнения.
В этом и есть профит, то что задача распараллеливается и любой поток может взять любую подзадачу для выполнения, даже если она была сгенерирована другим потоком.
Пример программы:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class ForkJoinExample {
static volatile int j;
public static void main(final String[] arguments)
throws InterruptedException,
ExecutionException {
//узнать количество ядер компьютера
//(в каждом ядре эффективно выполнять 1 поток)
int nThreads = Runtime.getRuntime().availableProcessors();
System.out.println(nThreads);
int[] numbers = new int[1000];
//запишем в массив числа от 1 до 1000
for(int i = 0; i < numbers.length; i++) {
numbers[i] = i+1;
}
//Будем складывать числа от 1 до 1000
//эффективным образом с помощью fork/join
// сюда передаем количество
// ядер (размер пула потоков)
ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
// invoke запускает задачу на выполнение
// то есть вызывает compile.
Long result = forkJoinPool.invoke(
new Sum(numbers,0,numbers.length));
//пока invoke не вернул значение дальше
//в методе мейн ничего не выполняется
System.out.println(result);
}
static class Sum extends RecursiveTask< Long > {
int low;
int high;
int[] array;
Sum(int[] array, int low, int high) {
this.array = array;
this.low = low;
this.high = high;
}
protected Long compute() {
//if задача или подзадача уже необходимо мала
//возвращаем что-то из метода compile
if(high – low <= 10) {
long sum = 0;
for(int i = low; i < high; ++i)
sum += array[i];
return sum;
}
//else - делим задачу или подзадачу
//на подзадачи (в данном случае на 2 половины)
else {
int mid = low + (high - low) / 2;
Sum left = new Sum(array, low, mid);
Sum right = new Sum(array, mid, high);
// добавляем одну половину задачи (подзадачу)
// в пул потоков
left.fork();
// добавляем вторую половину задачи (подзадачу)
// в пул потоков
right.fork();
// ждем когда compile в форке завершиться
long leftResult = left.join();
// ждем когда compile в форке завершиться
long rightResult = right.join();
return leftResult + rightResult;
}
}
}
}
Вывод:
Как можно увидеть, в консоли сумма чисел от 1 до 1000 посчитана верна – 500500.
Узнайте, как работает сериализация в Java: сохраняйте объекты в файлы с ObjectOutputStream и восстанавливайте их через ObjectInputStream. Пример кода и важность интерфейса Serializable.
Time to read: 10
Transient в Java
Ключевое слово transient в Java: как исключить поля из сериализации. Практическое руководство по работе с несериализуемыми данными.
Time to read: 8
Контроль совместимости классов с помощью SerialVersionUID
Используйте SerialVersionUID в Java для контроля версий классов при сериализации. Решение проблем совместимости при изменении структуры класса.