# Лабораторная №1
Разработка многопоточного приложения с использованием Java Concurrency
согласно варианту задания.
Необходимо:
1 Разработать однопоточный вариант алгоритма и замерить время его работы.
2  Разработать параллельный вариант алгоритма с использованием
ThreadPoolExecutor и замерить время его работы 
3  Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
и замерить время его работы.
**ВАЖНО**: Массив генерируется до работы всех вариантов алгоритмов. Все три
алгоритма обрабатывают три одинаковых массива. 
## Вариант и задание
22 Упорядочить столбцы матрицы по возрастанию первых элементов.
## Как запустить лабораторную работу
Минимальная требуемая версия jdk - 21.0.2
Для запуска лабораторной работы нужно в директории с java файлами (внутри папки src) выполнить следующее:
* >javac -d /путь/к/выходной/директории *.java
* перейти к выходной директории при помощи команды cd
* внутри директории с .class файлами нужно выполнить команду:
* >java Main

## Какие технологии использовались
В этой лабораторной использовалась следующие пакеты:
* java.util.concurrent — инструменты для многопоточности (пулы потоков, синхронизация).
* ForkJoinPool — пул потоков для параллельных задач по принципу "разделяй и властвуй".
* RecursiveAction — задача без возврата результата, которую можно разбить на подзадачи.
* Logger — журнал для логирования сообщений и ошибок.

## Что она делает
Согласно заданию программа создает матрицу состоящую из 5000 строчек и 5000 столбцов.
Программа реализует сортировку слиянием за тем исключением, что при перестановке перестановки осуществляются столбцами.
Я попытался создать некое подобие класса-управленца, ArrayController, но выглядит так, будто это лишнее. 
## Тесты
Для тестов был создан отдельный метод в классе Main, который прогоняет 2 матрицы для каждого алгоритма сортировки. 
Программа генерирует и сортирует большую матрицу только тогда, когда тесты успешно проходят.
Сами тесты:
#### Пример 1
##### Входные данные
``` 
{3,     8,    4, 10,  2,  1,102},
{2,   423,   41, 53,123,421,123},
{1312,214,    5,  1, 23,  4,  5}
```
##### Входные данные
```
{  1,   2,     3,  4,      8, 10, 102},
{421, 123,    2,  41,    423, 53, 123 },
{  4,  23, 1312,   5,    214, 1,   5}
```

#### Пример 2

##### Входные данные
```
{ 7,  12,   5,  2,  98, 33,  9},
{ 45,  3,  67, 89,   1,  4,  8},
{200, 50, 150, 25, 175, 75,  5}
```
##### Выходные данные
```
{ 2,   5,   7,   9,  12,  33,  98 },
{ 89, 67,  45,   8,   3,   4,   1 },
{ 25,150, 200,   5,  50,  75, 175 }
```
## Результаты работы на матрице 5000 на 5000
```
    Время выполнения без потоков: 3728.2977 мс
    Время выполнения с потоками ThreadExecutor: 1541.1539 мс
    Время выполнения с потоками ForkJoinPool: 1412.6938 мс
```
```
    Время выполнения без потоков: 4043.893 мс
    Время выполнения с потоками ThreadExecutor: 1624.9124 мс
    Время выполнения с потоками ForkJoinPool: 1474.203 мс
```
```
    Время выполнения без потоков: 3876.2693 мс
    Время выполнения с потоками ThreadExecutor: 1354.9933 мс
    Время выполнения с потоками ForkJoinPool: 1433.2023 мс
```
## Вывод
Оба варианта работа с потоками работают в более чем два раза быстрее чем без потоков.
Между собой 3 запуска программы показало, что в большинстве случаев ForkJoinPool справляется быстрее, чем 
ThreadExecutor. 
Скорее всего это связано с тем, что ForkJoinPool использует тот же принцип "Разделяй и влавствуй", 
что и сортировка слиянием.


