Zielony Smok - logo witryny

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

matrices034.zip

Moduł matrices – aktualny stan projektu = 034;