pyzhov_egor_lab_4 is ready #149
40
pyzhov_egor_lab_4/.gitignore
vendored
Normal file
40
pyzhov_egor_lab_4/.gitignore
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
!conf.xml
|
||||||
|
!myConf.xml
|
||||||
|
!pom.xml
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
20
pyzhov_egor_lab_4/conf.xml
Normal file
20
pyzhov_egor_lab_4/conf.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
|
||||||
|
<property name="igniteInstanceName" value="my-cluster"/>
|
||||||
|
<property name="peerClassLoadingEnabled" value="true"/>
|
||||||
|
<property name="cacheConfiguration">
|
||||||
|
<list>
|
||||||
|
<bean class="org.apache.ignite.configuration.CacheConfiguration">
|
||||||
|
<property name="name" value="myCache"/>
|
||||||
|
<property name="backups" value="1"/>
|
||||||
|
<property name="cacheMode" value="PARTITIONED"/>
|
||||||
|
</bean>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</beans>
|
||||||
35
pyzhov_egor_lab_4/myConf.xml
Normal file
35
pyzhov_egor_lab_4/myConf.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
|
||||||
|
<property name="igniteInstanceName" value="my-cluster"/>
|
||||||
|
<property name="peerClassLoadingEnabled" value="true"/>
|
||||||
|
<property name="discoverySpi">
|
||||||
|
<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
|
||||||
|
<property name="ipFinder">
|
||||||
|
<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
|
||||||
|
<property name="addresses">
|
||||||
|
<list>
|
||||||
|
<value>192.168.17.117:47500</value>
|
||||||
|
<value>192.168.17.118:47500</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="cacheConfiguration">
|
||||||
|
<list>
|
||||||
|
<bean class="org.apache.ignite.configuration.CacheConfiguration">
|
||||||
|
<property name="name" value="myCache"/>
|
||||||
|
<property name="backups" value="1"/>
|
||||||
|
<property name="cacheMode" value="PARTITIONED"/>
|
||||||
|
</bean>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</beans>
|
||||||
50
pyzhov_egor_lab_4/pom.xml
Normal file
50
pyzhov_egor_lab_4/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.urlshort</groupId>
|
||||||
|
<artifactId>sspr4</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>lab4</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.ignite</groupId>
|
||||||
|
<artifactId>ignite-core</artifactId>
|
||||||
|
<version>2.17.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.ignite</groupId>
|
||||||
|
<artifactId>ignite-indexing</artifactId>
|
||||||
|
<version>2.17.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.ignite</groupId>
|
||||||
|
<artifactId>ignite-spring</artifactId>
|
||||||
|
<version>2.17.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>2.7.18</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
171
pyzhov_egor_lab_4/readme.md
Normal file
171
pyzhov_egor_lab_4/readme.md
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# Лабораторная работа 4
|
||||||
|
|
||||||
|
## Задание
|
||||||
|
|
||||||
|
Разработка распределенного приложения с использованием платформы Apache Ignite.
|
||||||
|
Необходимо разработать параллельный вариант алгоритма с применением подхода Grid
|
||||||
|
Gain и платформа Apache Ignite, замерить время его работы.
|
||||||
|
|
||||||
|
**Вариант:** "Упорядочить строки матрицы по убыванию первых элементов."
|
||||||
|
|
||||||
|
## Описание работы программы
|
||||||
|
|
||||||
|
1. **Инициализация Ignite:** Запускает Ignite-кластер, используя конфигурационный файл `conf.xml`.
|
||||||
|
2. **Генерация матрицы:** Создает квадратную матрицу заданного размера (из аргументов командной строки).
|
||||||
|
3. **Распределенная сортировка матрицы:**
|
||||||
|
* Использует Ignite для распределенного выполнения задачи `MatrixSortingTask`.
|
||||||
|
* Задача `MatrixSortingTask` разделяет работу на подзадачи (`MatrixSortingJob`), распределяя обработку строк матрицы между узлами кластера.
|
||||||
|
4. **Разделение задачи(`split`)**
|
||||||
|
* Игнайт автоматически определяет количество узлов (gridSize).
|
||||||
|
* Матрица разбивается на блоки строк, каждый из которых обрабатывается отдельной подзадачей (MatrixSortingJob).
|
||||||
|
5. **Параллельная сортировка (`execute`)**
|
||||||
|
* Каждый узел получает свой диапазон строк, сортирует их по первому элементу (по убыванию) и возвращает результат.
|
||||||
|
6. **Объединение результатов (`reduce`)**
|
||||||
|
* Главный узел собирает все отсортированные блоки и выполняет финальную сортировку для получения итоговой матрицы.
|
||||||
|
7. **Вывод результата:**
|
||||||
|
* Если размер матрицы меньше 10x10, выводит отсортированную матрицу в консоль.
|
||||||
|
* В противном случае выводит сообщение о завершении сортировки.
|
||||||
|
* Замеряет и выводит время выполнения (duration).
|
||||||
|
## Описание кода
|
||||||
|
```java
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String workingDir = System.getProperty("user.dir") + File.separator + "conf.xml";
|
||||||
|
Path path = Paths.get(workingDir);
|
||||||
|
int size = Integer.parseInt(args[args.length - 1]);
|
||||||
|
|
||||||
|
try (Ignite ignite = Ignition.start(workingDir)) {
|
||||||
|
int[][] matrix = new int[size][size];
|
||||||
|
Random r = new Random();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Основной метод программы, который:
|
||||||
|
* Загружает конфигурацию Ignite из файла conf.xml
|
||||||
|
* Получает размер матрицы из аргументов командной строки
|
||||||
|
* Инициализирует Ignite-кластер
|
||||||
|
* Создает матрицу заданного размера и заполняет случайными числами
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static class MatrixSortingTask extends ComputeTaskSplitAdapter<int[][], int[][]> {
|
||||||
|
@Override
|
||||||
|
protected List<ComputeJob> split(int gridSize, int[][] matrix) {
|
||||||
|
List<ComputeJob> jobs = new ArrayList<>();
|
||||||
|
int batchSize = Math.max(1, matrix.length / gridSize);
|
||||||
|
```
|
||||||
|
Класс задачи, который Разделяет матрицу на части по количеству доступных узлов (gridSize) и создает подзадачи (MatrixSortingJob) для обработки каждой части.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static class MatrixSortingJob implements ComputeJob {
|
||||||
|
@Override
|
||||||
|
public int[][] execute() {
|
||||||
|
List<int[]> rows = new ArrayList<>();
|
||||||
|
for (int[] row : part) {
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
Collections.sort(rows, (row1, row2) -> Integer.compare(row2[0], row1[0]));
|
||||||
|
```
|
||||||
|
Класс подзадачи, который получает часть для обработки, сортирует и возвращает отсортированную часть.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public int[][] reduce(List<ComputeJobResult> results) {
|
||||||
|
List<int[]> sortedRows = new ArrayList<>();
|
||||||
|
for (ComputeJobResult res : results) {
|
||||||
|
int[][] part = res.getData();
|
||||||
|
Collections.sort(sortedRows, (row1, row2) -> Integer.compare(row2[0], row1[0]));
|
||||||
|
}
|
||||||
|
return sortedRows.toArray(new int[0][]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Метод получающий результаты со всех узлов, выполняет финальную сортировку объединенной матрицы.
|
||||||
|
|
||||||
|
|
||||||
|
## Запуск приложения
|
||||||
|
1) На обоих контейнерах должен быть установлен Apache Ignite.
|
||||||
|
```bush
|
||||||
|
wget https://dlcdn.apache.org/ignite/2.17.0/apache-ignite-2.17.0-bin.zip
|
||||||
|
unzip apache-ignite-2.17.0-bin.zip
|
||||||
|
```
|
||||||
|
2) На первом контейнере должен быть jar файл в одной папке с conf.xml, на втором в apache-ignite-2.17.0-bin/config должен находиться наш myConf.xml
|
||||||
|
3) Переходим на второй контейнер, который без jar файла и переходим в директорию apache-ignite-2.17.0-bin/bin, выполняем следующую команду:
|
||||||
|
```bash
|
||||||
|
ignite.sh -i
|
||||||
|
```
|
||||||
|
Выбираем myConf.xml
|
||||||
|
4) На контейнере с jar файлом запускаем программу
|
||||||
|
```bash
|
||||||
|
java -jar sspr4-1.0-SNAPSHOT.jar SIZE
|
||||||
|
```
|
||||||
|
|
||||||
|
## Основные технологии
|
||||||
|
|
||||||
|
- **Maven**
|
||||||
|
- **Apache Ignite**
|
||||||
|
- **Java 11**
|
||||||
|
|
||||||
|
## Результат работы программы
|
||||||
|
Запуск:
|
||||||
|
```bash
|
||||||
|
java -jar sspr4-1.0-SNAPSHOT.jar 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Вывод в первом контейнере:
|
||||||
|
```
|
||||||
|
Original matrix:
|
||||||
|
29 75 96 81 52
|
||||||
|
21 97 49 1 72
|
||||||
|
51 66 20 60 71
|
||||||
|
0 20 30 70 66
|
||||||
|
49 72 49 91 11
|
||||||
|
[MAIN] Created job for rows 0 to 1
|
||||||
|
[MAIN] Created job for rows 2 to 4
|
||||||
|
[Node-52c56677] Processing rows 0 to 1
|
||||||
|
[Node-52c56677] Part before sorting:
|
||||||
|
29 75 96 81 52
|
||||||
|
21 97 49 1 72
|
||||||
|
[Node-52c56677] Part after sorting:
|
||||||
|
29 75 96 81 52
|
||||||
|
21 97 49 1 72
|
||||||
|
|
||||||
|
Sorted matrix (by first element in descending order):
|
||||||
|
51 66 20 60 71
|
||||||
|
49 72 49 91 11
|
||||||
|
29 75 96 81 52
|
||||||
|
21 97 49 1 72
|
||||||
|
0 20 30 70 66
|
||||||
|
Execution time: 109 ms
|
||||||
|
[12:04:46] Ignite node stopped OK [name=my-cluster, uptime=00:00:00.327]
|
||||||
|
```
|
||||||
|
|
||||||
|
Вывод в втором контейенере:
|
||||||
|
```
|
||||||
|
[Node-85bfc0d0] Processing rows 2 to 4
|
||||||
|
[Node-85bfc0d0] Part before sorting:
|
||||||
|
51 66 20 60 71
|
||||||
|
0 20 30 70 66
|
||||||
|
49 72 49 91 11
|
||||||
|
[Node-85bfc0d0] Part after sorting:
|
||||||
|
51 66 20 60 71
|
||||||
|
49 72 49 91 11
|
||||||
|
0 20 30 70 66
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Были проведены замеры работы программы на разных объёмах данных:
|
||||||
|
|
||||||
|
Замеры с использованием двух узлов:
|
||||||
|
- size 1000: 171 ms
|
||||||
|
- size 3000: 967 ms
|
||||||
|
- size 5000: 1881 ms
|
||||||
|
- size 7000: 4744 ms
|
||||||
|
|
||||||
|
Замеры с выполнением на одном узле:
|
||||||
|
- size 1000: 32 ms
|
||||||
|
- size 3000: 29 ms
|
||||||
|
- size 5000: 50 ms
|
||||||
|
- size 7000: 68 ms
|
||||||
|
|
||||||
|
## Вывод
|
||||||
|
|
||||||
|
Из полученных результатов можно сделать вывод, что в нашем случае, распределение вычислений между контейенерами привело к значительному замедлению во всех случаях. Накладные расходы на координацию между узлами занимают гораздо большее время, чем сам процесс сортировки.
|
||||||
|
|
||||||
156
pyzhov_egor_lab_4/src/main/java/MatrixApp.java
Normal file
156
pyzhov_egor_lab_4/src/main/java/MatrixApp.java
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import org.apache.ignite.Ignite;
|
||||||
|
import org.apache.ignite.IgniteException;
|
||||||
|
import org.apache.ignite.Ignition;
|
||||||
|
import org.apache.ignite.compute.ComputeTaskSplitAdapter;
|
||||||
|
import org.apache.ignite.compute.ComputeJob;
|
||||||
|
import org.apache.ignite.compute.ComputeJobResult;
|
||||||
|
import org.apache.ignite.resources.IgniteInstanceResource;
|
||||||
|
import org.apache.ignite.cluster.ClusterNode;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class MatrixApp {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String workingDir = System.getProperty("user.dir") + File.separator + "conf.xml";
|
||||||
|
Path path = Paths.get(workingDir);
|
||||||
|
int size = Integer.parseInt(args[args.length - 1]);
|
||||||
|
|
||||||
|
try (Ignite ignite = Ignition.start(workingDir)) {
|
||||||
|
int[][] matrix = new int[size][size];
|
||||||
|
Random r = new Random();
|
||||||
|
|
||||||
|
System.out.println("Original matrix:");
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
for (int j = 0; j < size; j++) {
|
||||||
|
matrix[i][j] = r.nextInt(100);
|
||||||
|
System.out.print(matrix[i][j] + "\t");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
int[][] sortedMatrix = ignite.compute().execute(new MatrixSortingTask(), matrix);
|
||||||
|
long duration = System.currentTimeMillis() - start;
|
||||||
|
|
||||||
|
System.out.println("\nSorted matrix (by first element in descending order):");
|
||||||
|
for (int[] row : sortedMatrix) {
|
||||||
|
for (int value : row) {
|
||||||
|
System.out.print(value + "\t");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Execution time: " + duration + " ms");
|
||||||
|
} catch (IgniteException e) {
|
||||||
|
System.err.println("Ignite Exception: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("General Exception: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MatrixSortingTask extends ComputeTaskSplitAdapter<int[][], int[][]> {
|
||||||
|
@IgniteInstanceResource
|
||||||
|
private Ignite ignite;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<ComputeJob> split(int gridSize, int[][] matrix) {
|
||||||
|
List<ComputeJob> jobs = new ArrayList<>();
|
||||||
|
int batchSize = Math.max(1, matrix.length / gridSize);
|
||||||
|
|
||||||
|
for (int i = 0; i < gridSize; i++) {
|
||||||
|
int start = i * batchSize;
|
||||||
|
int end = (i == gridSize - 1) ? matrix.length : (i + 1) * batchSize;
|
||||||
|
|
||||||
|
if (start < matrix.length) {
|
||||||
|
jobs.add(new MatrixSortingJob(matrix, start, end));
|
||||||
|
System.out.println("[MAIN] Created job for rows " + start + " to " + (end - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[][] reduce(List<ComputeJobResult> results) {
|
||||||
|
List<int[]> sortedRows = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ComputeJobResult res : results) {
|
||||||
|
int[][] part = res.getData();
|
||||||
|
for (int[] row : part) {
|
||||||
|
sortedRows.add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(sortedRows, (row1, row2) -> Integer.compare(row2[0], row1[0]));
|
||||||
|
|
||||||
|
return sortedRows.toArray(new int[0][]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MatrixSortingJob implements ComputeJob {
|
||||||
|
@IgniteInstanceResource
|
||||||
|
private Ignite ignite;
|
||||||
|
|
||||||
|
private final int[][] matrix;
|
||||||
|
private final int start;
|
||||||
|
private final int end;
|
||||||
|
|
||||||
|
public MatrixSortingJob(int[][] matrix, int start, int end) {
|
||||||
|
this.matrix = matrix;
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[][] execute() {
|
||||||
|
ClusterNode node = ignite.cluster().localNode();
|
||||||
|
UUID nodeId = node.id();
|
||||||
|
String nodeName = "Node-" + nodeId.toString().substring(0, 8);
|
||||||
|
|
||||||
|
System.out.println("[" + nodeName + "] Processing rows " + start + " to " + (end - 1));
|
||||||
|
|
||||||
|
int[][] part = new int[end - start][];
|
||||||
|
System.arraycopy(matrix, start, part, 0, end - start);
|
||||||
|
|
||||||
|
System.out.println("[" + nodeName + "] Part before sorting:");
|
||||||
|
for (int[] row : part) {
|
||||||
|
for (int value : row) {
|
||||||
|
System.out.print(value + "\t");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int[]> rows = new ArrayList<>();
|
||||||
|
for (int[] row : part) {
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(rows, (row1, row2) -> Integer.compare(row2[0], row1[0]));
|
||||||
|
|
||||||
|
System.out.println("[" + nodeName + "] Part after sorting:");
|
||||||
|
for (int[] row : rows) {
|
||||||
|
for (int value : row) {
|
||||||
|
System.out.print(value + "\t");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows.toArray(new int[0][]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
System.out.println("Job was cancelled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user