Files
SSPR_25/kuznetsov_danila_lab_1
..
2025-03-12 17:19:08 +04:00
2025-03-12 17:19:08 +04:00

1 Лабораторная работа

Задание

Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.

Необходимо:

  1. Разработать однопоточный вариант алгоритма и замерить время его работы.
  2. Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы.
  3. Разработать параллельный вариант алгоритма с использованием ForkJoinPool и замерить время его работы.

Вариант: "Определить минимальный элемент матрицы ниже главной диагонали"


Подготовка

Генерация матрицы

private static int[][] generateMatrix(int n) {
    int[][] matrix = new int[n][n];
    Random random = new Random();
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            matrix[i][j] = random.nextInt(20001) - 10000;
        }
    }
    return matrix;
}

Описание:
Метод получает размер матрицы n и создаёт массив размером n x n, заполняя его числами от -10000 до 10000 (так как random.nextInt(20001) возвращает значения от 0 до 20000, а затем вычитается 10000).


Пункт 1. Однопоточный вариант

public static int findMinSingleThread(int[][] matrix) {
    int n = matrix.length;
    int min = Integer.MAX_VALUE;
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (matrix[i][j] < min) {
                min = matrix[i][j];
            }
        }
    }
    return min;
}

Описание:
Создаётся переменная min, инициализированная максимальным значением типа int. Далее происходит последовательный проход по элементам матрицы ниже главной диагонали (для строки i перебираются столбцы с 0 до i-1), и если найден элемент меньше текущего минимума, переменная min обновляется.


Пункт 2. Параллельный вариант с использованием ThreadPoolExecutor

public static int findMinThreadPool(int[][] matrix) {
    int n = matrix.length;
    int numThreads = Runtime.getRuntime().availableProcessors();
    ExecutorService executor = Executors.newFixedThreadPool(numThreads);
    List<Future<Integer>> futures = new ArrayList<>();

    int blockSize = (n + numThreads - 1) / numThreads;
    for (int t = 0; t < numThreads; t++) {
        int startRow = t * blockSize;
        int endRow = Math.min(n, startRow + blockSize);
        Future<Integer> future = executor.submit(() -> {
            int localMin = Integer.MAX_VALUE;
            for (int i = Math.max(startRow, 1); i < endRow; i++) {
                for (int j = 0; j < i; j++) {
                    if (matrix[i][j] < localMin) {
                        localMin = matrix[i][j];
                    }
                }
            }
            return localMin;
        });
        futures.add(future);
    }

    int min = Integer.MAX_VALUE;
    try {
        for (Future<Integer> future : futures) {
            int result = future.get();
            if (result < min) {
                min = result;
            }
        }
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
    executor.shutdown();
    return min;
}

Описание:

  • Получаем число доступных процессоров и сохраняем его в переменную numThreads.
  • С помощью Executors.newFixedThreadPool(numThreads) создаём пул фиксированного размера.
  • Рассчитываем размер блока строк:
    int blockSize = (n + numThreads - 1) / numThreads;
    
    Таким образом, матрица делится на равные (или почти равные) части.
  • Для каждого блока определяется диапазон строк от startRow до endRow. В лямбда-выражении происходит последовательный поиск минимума в этом диапазоне (начиная с Math.max(startRow, 1), чтобы не обрабатывать строку 0).
  • Результаты задач собираются в список futures, после чего перебираются и вычисляется глобальный минимум.
  • После получения результатов пул потоков закрывается через executor.shutdown().

Пункт 3. Параллельный вариант с использованием ForkJoinPool

public static int findMinForkJoin(int[][] matrix) {
    ForkJoinPool forkJoinPool = new ForkJoinPool();
    MatrixMinTask task = new MatrixMinTask(matrix, 1, matrix.length);
    int min = forkJoinPool.invoke(task);
    forkJoinPool.shutdown();
    return min;
}

static class MatrixMinTask extends RecursiveTask<Integer> {
    private static final int THRESHOLD = 100;
    private int[][] matrix;
    private int startRow;
    private int endRow;

    public MatrixMinTask(int[][] matrix, int startRow, int endRow) {
        this.matrix = matrix;
        this.startRow = startRow;
        this.endRow = endRow;
    }

    @Override
    protected Integer compute() {
        if (endRow - startRow <= THRESHOLD) {
            int localMin = Integer.MAX_VALUE;
            for (int i = startRow; i < endRow; i++) {
                for (int j = 0; j < i; j++) {
                    if (matrix[i][j] < localMin) {
                        localMin = matrix[i][j];
                    }
                }
            }
            return localMin;
        } else {
            int mid = (startRow + endRow) / 2;
            MatrixMinTask leftTask = new MatrixMinTask(matrix, startRow, mid);
            MatrixMinTask rightTask = new MatrixMinTask(matrix, mid, endRow);
            leftTask.fork();                   
            int rightResult = rightTask.compute();
            int leftResult = leftTask.join();  
            return Math.min(leftResult, rightResult);
        }
    }
}

Описание:

  • Создаётся экземпляр ForkJoinPool для распараллеливания рекурсивных задач.
  • Запускается задача MatrixMinTask, которая ищет минимум в строках с startRow = 1 до matrix.length.
  • В классе MatrixMinTask определён порог THRESHOLD = 100. Если диапазон строк меньше или равен порогу, задача решается последовательно. В противном случае диапазон делится на две части:
    • Левая задача: обрабатывает строки от startRow до mid.
    • Правая задача: обрабатывает строки от mid до endRow.
  • Для асинхронного выполнения используется метод fork() (для левой задачи), а затем с помощью compute() и join() происходит объединение результатов двух подзадач.

Запуск

Запустить jar можно командой:

java -jar Lab-1-jar-with-dependencies.jar