Macierze i wielowątkowość (Java)
W przypadku wykonywania złożonych i długich czynności niezbędne jest korzystanie a systemu wątków. Dawniej używało się jedynie obiektów dziedziczących po Thread
lub implementujących Runnable
.
Dzisiaj używa się nowych rozwiązań.
Zdecydowanie najbardziej użyteczną klasą jest klasa RecursiveRask<T>
. Umożliwia w pełni równoległe obliczenia. Poniżej pokazujemy ją w różnych zastosowaniach. Pozwala ona na korzystanie z obiektów implementujących Runnable
.
Obliczanie wyznacznika macierzy
Przykład w klasie Matrix073
:
package matrices2; import matrices.Matrix; import matrices.MatrixException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; public class Matrix073 extends RecursiveTask<Double> { private static final long serialVersionUID = 2613196985818202539L; private final String path; public Matrix073(String path) { this.path = path; } @Override protected Double compute() { Matrix matrix = new Matrix(path); Double det = null; try { det = matrix.det(); } catch (MatrixException e) { e.printStackTrace(); } return det; } public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); Matrix073 task = new Matrix073(" matrices/assets/matrix6.txt"); double det = pool.invoke(task); System.out.println(det); } }
Po uruchomieniu klasy macierz zostanie odczytana z pliku matrix6.txt. Obliczony
wyznacznik macierzy zostanie pokazany na konsoli.
-69605.0
Mnożenie macierzy
Przykład w klasie Matrix074
.
package matrices2; import matrices.Matrix; import matrices.MatrixUtil; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; public class Matrix074 extends RecursiveTask<Matrix> { private static final long serialVersionUID = 2613196985818202539L; private final String path1; private final String path2; public Matrix074(String path1, String path2) { this.path1 = path1; this.path2 = path2; } @Override protected Matrix compute() { Matrix matrix1 = new Matrix(path1); Matrix matrix2 = new Matrix(path2); return MatrixUtil.multiply3(matrix1, matrix2); } public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); Matrix074 task = new Matrix074(" matrices/assets/matrix6.txt", "matrices/assets/matrix6.txt"); Matrix mat = pool.invoke(task); mat.printToConsole(); } }
Po uruchomieniu klasy na konsoli zobaczymy macierz będąca wynikiem mnożenia:
66.0 15.0 -85.0 -39.0 25.0 126.0 -3.0 4.0 40.0 54.0 88.0 56.0 -70.0 18.0 53.0 -78.0 -47.0 54.0 14.0 1.0 8.0 124.0 50.0 -35.0 85.0 18.0 -89.0 56.0 4.0 -9.0 22.0 23.0 23.0 0.0 31.0 -2.0
Łączenie wielu wyników
Przykład w klasie Matrix075:
Obliczanie sumy średnich rzędów macierzy. Jeżeli liczba rzędów jest mniejsza od 4 to liczenie wykonywane jest iteracyjnie. Jeżeli jest większa, zadanie dzielone jest na dwa podzadania.
package matrices2; import matrices.Matrix; import matrices.MatrixUtil; import matrices.StatUtil; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; public class Matrix075 extends RecursiveTask<Double> { private static final long serialVersionUID = 2613196985818202539L; private final Matrix matrix; public Matrix075(Matrix matrix) { this.matrix = matrix; } @Override protected Double compute() { double[][] tabl = matrix.getArray(); int rows = tabl.length; int cols = tabl[0].length; double sum = 0.0; if (rows < 4) { for (double[] doubles : tabl) { sum += StatUtil.mean(doubles); } } else { Matrix075 a = new Matrix075(MatrixUtil. submatrix(matrix, 0, rows / 2, 0, cols - 1)); a.fork(); Matrix075 b = new Matrix075(MatrixUtil. submatrix(matrix, rows / 2 + 1, rows - 1, 0, cols - 1)); b.fork(); sum = a.join() + b.join(); } return sum; } public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); Matrix075 task = new Matrix075(new Matrix(" matrices/assets/matrix6.txt")); double sum = pool.invoke(task); System.out.println(sum); } }
Po uruchomieniu klasy na konsoli otrzymujemy:
10.833333333333334
Metoda invoke()
wywołuje zadania synchronicznie, tj. czeka na zakończenie zadania. Aby wykonać niektóre zadania asynchronicznie należy użyć metody execute()
. Przy użyciu tej drugiej metody możemy uruchomić wątek klasy implementującej Runnable
albo ForkJoinTask
.
Pliki do ściągnięcia
Moduł matrices – aktualny stan projektu = 034;