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

Разработка многопоточного приложения с использованием Java Concurrency.
Мой вариант -> 4) Определить минимальный элемент матрицы.

## Цель

Сравнить время выполнения трех реализаций алгоритма поиска минимального элемента в большой матрице:
1. Однопоточный алгоритм.
2. Многопоточный алгоритм с использованием `ThreadPoolExecutor`.
3. Многопоточный алгоритм с использованием `ForkJoinPool`.

Все три алгоритма обрабатывают один и тот же массив, который генерируется один раз перед запуском.

---

## Описание программы

1. **Генерация матрицы**  
   Метод `generateMatrix(...)` создает матрицу размером `ROWS x COLS`, заполняя ее случайными числами, используя `Random.nextLong(MaxValue)`.

2. **Однопоточный поиск минимума**  
   - `findMinSingleThread(...)` обходит все строки и для каждой строки вызывает `findMinInRaw(...)` (локальный поиск минимума в одномерном массиве).
   - Возвращается общий минимальный элемент по всей матрице.

3. **Поиск минимума с `ThreadPoolExecutor`**  
   - `findMinThreadPool(...)` создает пул потоков из `THREADS`.
   - Для каждой строки матрицы отправляет задачу (`Callable<Long>`), которая в свою очередь вызывает `findMinInRaw(...)` для поиска минимума в одной строке.
   - Собираются результаты всех `Future<Long>`, ищется минимальный элемент из их значений.

4. **Поиск минимума с `ForkJoinPool`**  
   - `findMinForkJoin(...)` создает `ForkJoinPool` и запускает рекурсивную задачу `MinTask`.
   - `MinTask` (наследует `RecursiveTask<Long>`) делит промежуток строк на две части, пока не достигнет порога `MAX_ROWS`. Когда участок достаточно мал, последовательно ищет минимум по строкам.
   - Объединяет (берет минимум) из двух подзадач.

5. **Замер времени**  
   - В `main(...)` для каждого из трех алгоритмов замеряется время до и после выполнения (через `System.nanoTime()`).
   - Результаты выводятся в консоль.

---

## Как запустить

1. Установить JDK (Java 8 или выше).  
2. Скомпилировать:
   ```bash
   javac Main.java
   ```
3. Запустить:
   ```bash
   java Main
   ```
4. По завершении вы увидите три строки с минимальным элементом и временем выполнения (в мс) для:
   - Однопоточного алгоритма
   - Алгоритма с `ThreadPoolExecutor`
   - Алгоритма с `ForkJoinPool`

---

## Пример вывода

```
Single: Min number - 0, Time: 107 мс
ThreadPoolExecutor: Min number - 0, Time: 102 мс
ForkJoinPool: Min number - 0, Time: 63 мс
```

Числа зависят от размера матрицы и мощности процессора.
Чтобы было более наглядно видно разницу по времени между всеми реализации алгоритма, нужно более большую матрицу.

---

## Вывод
1. **Однопоточный** вариант прост в реализации, но при больших размерах матрицы работает долго.
2. **ThreadPoolExecutor** позволяет распараллелить обработку строк матрицы и значительно ускорить поиск.
3. **ForkJoinPool** часто дает еще более высокое ускорение за счет механизма рекурсивного разбиения задач, но реальная производительность зависит от конфигурации процессоров и параметров разбиения.

