Shipilov_Nikita_lab_1

This commit is contained in:
2025-03-21 17:30:12 +04:00
parent c3d5ac3ba2
commit 9307b34c0d
3 changed files with 249 additions and 0 deletions

View File

@@ -0,0 +1,123 @@
import java.util.Random;
import java.util.concurrent.*;
public class Main {
public static int[][] generateMatrix(int rows, int cols) {
int[][] matrix = new int[rows][cols];
Random random = new Random();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = random.nextInt(100) + 1;
}
}
return matrix;
}
public static int findMin(int[][] matrix) {
int min = Integer.MAX_VALUE;
for (int[] row : matrix) {
for (int value : row) {
if (value < min) {
min = value;
}
}
}
return min;
}
public static void singleThreadedDivision(int[][] matrix) {
int min = findMin(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= min;
}
}
}
public static void threadPoolDivision(int[][] matrix, int numThreads) {
int min = findMin(matrix);
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < matrix.length; i++) {
final int row = i;
executor.submit(() -> {
for (int j = 0; j < matrix[row].length; j++) {
matrix[row][j] /= min;
}
});
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void forkJoinDivision(int[][] matrix) {
int min = findMin(matrix);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new DivideTask(matrix, min, 0, matrix.length));
}
private static class DivideTask extends RecursiveAction {
private final int[][] matrix;
private final int min;
private final int start;
private final int end;
DivideTask(int[][] matrix, int min, int start, int end) {
this.matrix = matrix;
this.min = min;
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if (end - start <= 10) {
for (int i = start; i < end; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= min;
}
}
} else {
int mid = (start + end) / 2;
invokeAll(new DivideTask(matrix, min, start, mid),
new DivideTask(matrix, min, mid, end));
}
}
}
public static void main(String[] args) {
int rows = 1000;
int cols = 1000;
int[][] matrix = generateMatrix(rows, cols);
int[][] singleThreadedMatrix = copyMatrix(matrix);
long startTime = System.currentTimeMillis();
singleThreadedDivision(singleThreadedMatrix);
long endTime = System.currentTimeMillis();
System.out.println("Single thread alghoritm: " + (endTime - startTime) + " ms");
int[][] threadPoolMatrix = copyMatrix(matrix);
startTime = System.currentTimeMillis();
threadPoolDivision(threadPoolMatrix, 4);
endTime = System.currentTimeMillis();
System.out.println("ThreadPoolExecutor: " + (endTime - startTime) + " ms");
int[][] forkJoinMatrix = copyMatrix(matrix);
startTime = System.currentTimeMillis();
forkJoinDivision(forkJoinMatrix);
endTime = System.currentTimeMillis();
System.out.println("ForkJoinPool: " + (endTime - startTime) + " ms");
}
public static int[][] copyMatrix(int[][] matrix) {
int[][] copy = new int[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; i++) {
System.arraycopy(matrix[i], 0, copy[i], 0, matrix[i].length);
}
return copy;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,126 @@
# Лабораторная работа №1
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
### Необходимо:
- Разработать однопоточный вариант алгоритма и замерить время его работы.
- Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы.
- Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
!!!. Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
Вариант задания 2: Разделить элементы матрицы на наименьший элемент.
### Как запустить лабораторную работу:
`javac Main.java` компилирует исходный код
`java Main` команда запускает скомпилированный класс
### Какие технологии использовали:
- Java 17+
- ExecutorService (ThreadPoolExecutor)
- ForkJoinPool
- System.currentTimeMillis() для замеров времени выполнения
### Как работает программа:
1. Генерация матрицы:
- Программа создаёт двумерный массив заданного размера (например, 1000x1000).
- Заполняет его случайными значениями.
- Определяет наименьший элемент матрицы.
2. Однопоточный алгоритм:
- Проходит по всей матрице в одном потоке.
- Делит каждый элемент матрицы на наименьший элемент.
- Записывает результат в новую матрицу.
- Измеряет время выполнения.
3. Параллельный алгоритм с ThreadPoolExecutor:
- Создаёт пул потоков с фиксированным количеством потоков (обычно равное количеству ядер CPU).
- Делит матрицу на строки, назначая обработку каждой строки отдельному потоку.
- Потоки выполняют деление элементов и записывают результаты.
- Ожидает завершения всех потоков.
- Измеряет время выполнения.
4. Параллельный алгоритм с ForkJoinPool:
- Использует принцип рекурсивного разбиения задач (Fork/Join).
- Разбивает матрицу на более мелкие подзадачи, пока они не станут достаточно малы для обработки в одном потоке.
- Выполняет деление элементов параллельно.
- Объединяет результаты.
- Измеряет время выполнения.
5. Вывод результатов:
- Для каждого метода измеряется время выполнения.
- Результаты выводятся в консоль.
### (Размер матрицы 1000х1000)
```bash
Single thread alghoritm: 20 ms
ThreadPoolExecutor: 73 ms
ForkJoinPool: 28 ms
```
### (Размер матрицы 2000х2000)
```bash
Single thread alghoritm: 39 ms
ThreadPoolExecutor: 110 ms
ForkJoinPool: 51 ms.
```
### (Размер матрицы 3000x3000)
```bash
Single thread alghoritm: 69 ms
ThreadPoolExecutor: 182 ms
ForkJoinPool: 106 ms
```
### (Размер матрицы 4000x4000)
```bash
Single thread alghoritm: 108 ms
ThreadPoolExecutor: 237 ms
ForkJoinPool: 134 ms
```
<img src="./diagram.png">
### Анализ результатов
1. Однопоточный алгоритм показывает линейный рост времени выполнения с увеличением размера матрицы. Это ожидаемо, так как вычисления выполняются последовательно.
2. `ThreadPoolExecutor` демонстрирует худшие результаты по сравнению с остальными методами. Это связано с накладными расходами на управление потоками и передачу задач в пул потоков. Он эффективен при большом количестве независимых задач, но в данном случае не даёт прироста.
3. `ForkJoinPool` показывает лучшую производительность среди параллельных методов. Это объясняется тем, что `ForkJoinPool` оптимизирован для рекурсивного разбиения задач и эффективного использования ресурсов CPU.
4. При увеличении размера матрицы разница между `ForkJoinPool` и ThreadPoolExecutor становится более заметной, так как `ForkJoinPool` минимизирует накладные расходы на распределение задач.
### Вывод
Использование `ForkJoinPool` даёт наилучший результат среди многопоточных методов в данном алгоритме. `ThreadPoolExecutor` не оправдывает себя из-за накладных расходов на управление потоками. Однопоточный метод показывает неплохие результаты на небольших данных, но проигрывает `ForkJoinPool` при увеличении объёма вычислений.