forked from sevastyan_b/SSPR_25
Лабораторная работа №1
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
Необходимо:
- Разработать однопоточный вариант алгоритма и замерить время его работы.
- Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы
- Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы. Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
Вариант 12.
Определить среднее арифметическое элементов матрицы ниже главной диагонали.
Запуск лаборатороной работы:
- Компиляция программы осуществляется с помощью команды:
javac Main.java
- Запуск скомпилированного класса:
java Main
Использованные технологии:
- Пакет
java.util.concurrentпредоставляет мощные инструменты для работы с многопоточностью и параллельными вычислениями ExecutorServiceиExecutorsиспользуются для управления пулами потоков и выполнения задачThreadPoolExecutor- в этом подходе используется пул потоков для параллельного вычисления суммы элементов матрицы. Матрица разбивается на части, и каждая часть обрабатывается отдельным потоком.ForkJoinPool— специализированный пул потоков, предназначенный для выполнения задач, которые могут быть рекурсивно разбиты на более мелкие подзадачи (fork), а затем объединены (join).
Как работает программа:
- Происходит генерация массива размером = size в классе
Main.java
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
int size = 1000; // size matrix
int[][] matrix = generateMatrix(size);
Метод для генерации массива:
private static int[][] generateMatrix(int size) {
int[][] matrix = new int[size][size];
Random random = new Random();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = random.nextInt(1000);
}
}
return matrix;
}
- Создание трех копий исходного массива.
- Происходит обработка массива с помощью однопоточного варианта алгоритма ( класс SingleThreadedAverage).
- Класс ThreadPoolAverage обрабатывает матрицу с использованием пула потоков создается пул потоков (задается количество потоков = 4), а каждая строка матрицы обрабатывается в отдельном потоке.
- Класс ForkJoinAverage обрабатывает матрицу с использованием ForkJoinPool и рекурсивного разделения задачи: если блок большой, он разделяется на две части, и каждая часть обрабатывается рекурсивно.
- Происходит замер времени работы всех алгоритмов в классе
Main.java:
// SingleThreaded algorithm
long startTime = System.nanoTime();
double singleThreadedResult = SingleThreadedAverage.calculate(matrix);
long endTime = System.nanoTime();
System.out.println("Result: " + singleThreadedResult);
System.out.println("Single-threaded time: " + (endTime - startTime) / 1_000_000 + " ms");
// ThreadPoolExecutor algorithm
startTime = System.nanoTime();
double threadPoolResult = ThreadPoolAverage.calculate(matrix, 4);
endTime = System.nanoTime();
System.out.println("ThreadPoolExecutor time: " + (endTime - startTime) / 1_000_000 + " ms");
// ForkJoinPool algorithm
startTime = System.nanoTime();
double forkJoinResult = ForkJoinAverage.calculate(matrix);
endTime = System.nanoTime();
System.out.println("ForkJoinPool time: " + (endTime - startTime) / 1_000_000 + " ms");
Тесты:
- при size = 1000: Result: 498.8887907907908 Single-threaded time: 4 ms ThreadPoolExecutor time: 104 ms ForkJoinPool time: 42 ms
Result: 499.8382882882883 Single-threaded time: 1 ms ThreadPoolExecutor time: 35 ms ForkJoinPool time: 15 ms
- при size = 10000: Result: 499.5273448344835 Single-threaded time: 26 ms ThreadPoolExecutor time: 42 ms ForkJoinPool time: 18 ms
Вывод:
При небольших размерах матрицы лучше использовать однопоточный вариант алгоритма, так как он дает лучший показатель времени. Однако, если размер генерируемой матрицы большой, то нужно использовать ForkJoinPool. А ThreadPoolExecutor - самый медленный.