用于乘法矩阵的同步线程

时间:2012-11-21 14:19:42

标签: java multithreading synchronization matrix-multiplication

我整晚都在研究这个问题并且没有找到解决方案,所以如果有人能帮助我,我真的很感激!我可能错过了一些非常明显的东西。这是一个理解同步的赋值,我们在前面的赋值中使用线程来乘以2个矩阵。在前面的赋值中,每个线程乘以一行,因此线程数与行数一样多。

在这个赋值中,我们只应该使用5个线程 - 所有线程都应该以一行/一列开始,一旦线程完成,它应该使用同步选择下一个可用的行/列,所以现在两个线程将结束做同一列。

This question帮助我找到了正确的方向,但我必须对实施做错事,因为到目前为止,我只是将程序运用到:

  1. 只执行前5行 - 5个线程执行一次,每个线程计算一行或
  2. 我添加了一个循环(现在在我的代码中注释掉了),所以线程会继续执行,但是当我这样做时,只有第一个线程可以正常工作。
  3. 这是我的主要课程和一些辅助方法:

    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Random;
    import java.util.Scanner;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.locks.Lock;
    
    public class MatrixMult {
    
    public static void main(String[] args){
        int[][] matrixA;
        int[][] matrixB;
        int colA = 0;
        int rowA = 0;
        int colB = 0;
        int rowB = 0;
        Scanner userInput = new Scanner( System.in );
        System.out.println("Please enter the dimensions of matrix A");
    
        do{
            System.out.print("column for matrix A: ");
            colA = userInput.nextInt();
            System.out.println();
        } while(!validDimension(colA));
    
        rowB = colA;
    
        do{
            System.out.print("row for matrix A: ");
            rowA = userInput.nextInt();
            System.out.println();
        } while(!validDimension(rowA));
    
        matrixA = new int[rowA][colA];
    
        System.out.println("Please enter the dimensions of matrix B:");
        do{
            System.out.print("column for matrix B: ");
            colB = userInput.nextInt();
            System.out.println();
        } while(!validDimension(colB));
    
        matrixB = new int[rowB][colB];
    
    
        fillMatrix(matrixA);
        fillMatrix(matrixB);
    
        System.out.println("Would you like to print out matrix A and B? (y/n)");
        String userResponse = userInput.next();
        if(userResponse.equalsIgnoreCase("y")){
            System.out.println("Matrix A:");
            printBackMatrix(matrixA);
            System.out.println();
            System.out.println("Matrix B:");
            printBackMatrix(matrixB);
            System.out.println();
        }
    
    
        int[][] matrixProduct3 = multMatrixWithThreadsSync(matrixA, matrixB);
    
        String fileName = "C:/matrix.txt";
        System.out.println("Matrix product is being written to "+fileName);
        try {
            printMatrixToFile(matrixProduct3, fileName);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    private static int[][] multMatrixWithThreadsSync(int[][] matrixA, int[][] matrixB) {
    
        int[][] matrixProduct = new int[matrixA.length][matrixB[0].length];
        int[] matrixProductColumn = new int[matrixA.length];
    
        Runnable task = new MultMatrixByRow(matrixA, matrixB, matrixProduct);
    
        for(int i=0; i<5; i++){
    
            Thread worker = new Thread(task);
            worker.start();
    //          System.out.println(worker.getName());
            try {
                worker.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return matrixProduct;
    }
    
    private static void printMatrixToFile(int[][] matrix, String fileName) throws IOException{
        PrintWriter userOutput = new PrintWriter(new FileWriter(fileName));
        for(int i=0; i<matrix.length; i++){
            for(int j=0; j<matrix[0].length; j++){
                userOutput.print(matrix[i][j]+" ");
            }
            userOutput.println();
        }
        userOutput.close();
    
    }
    
    private static void printBackMatrix(int[][] matrix) {
        for(int i=0; i<matrix.length; i++){
            for(int j=0; j<matrix[0].length; j++){
                System.out.print(matrix[i][j]+" ");
            }
            System.out.println();
        }
    }
    
    private static void fillMatrix(int[][] matrix) {
        Random rand = new Random();
    
        for(int i=0; i<matrix.length; i++){
            for(int j=0; j<matrix[0].length; j++){
                matrix[i][j] = rand.nextInt(100) + 1;
            }
        }
    
    }
    
    public static boolean validDimension(int dim){
        if (dim <= 0 || dim >1000){
            System.err.println("Dimension value entered is not valid");
            return false;
        }
        return true;
    
    }
    }
    

    这是我的runnable类:

    public class MultMatrixByRow implements Runnable {
    private int i;
    private int[][] matrixA;
    private int[][] matrixB;
    private int[][] matrixProduct;
    
    public MultMatrixByRow(int[][] A, int[][] B, int[][] C) {
        this.matrixA = A;
        this.matrixB = B;
        this.matrixProduct = C;
    }
    
    @Override   
    public void run(){
    //      while(i < matrixProduct.length){
            int rowToWork = 0;
            synchronized (this){
     //             System.out.println("i is "+i);
                if ( i < matrixProduct.length){
                    rowToWork = i;
                    i++;
                }
                else{
                    return;
                }
            }
            for(int j = 0; j < matrixB[0].length; j++){
                for(int k=0; k < matrixA[0].length; k++){
                    matrixProduct[rowToWork][j] += matrixA[rowToWork][k]*matrixB[k][j];
                }
            }
    //      }
            }
        }
    

    再次 - 真的很感激任何帮助!非常感谢。

4 个答案:

答案 0 :(得分:3)

  1. 您没有在资源上进行同步,您需要共享锁定对象(在静态上下文中或通过构造函数)
  2. 当你甚至不让它们同步工作时,我真的无法弄清楚你的程序中应该同步什么....你启动一个线程并直接等待他停止。 我认为你必须首先启动它们,然后在另一个循环中调用每个线程上的连接。
  3. 另外,我不太确定你的Threads应该单独解决什么,我认为它们都可以解决整个产品矩阵。您需要共享一个用于标识已处理行的变量,您可以访问这些行。

    我可以修复你的代码,但我很感激你自己做这项工作,因为理解线程并发是一项任务。

    编辑:同步说明:
    Synchronized将一个对象作为一个锁,只有一个线程可以为它保存监视器。当有监视器用于锁定时,线程可以处理该块,如果没有,他必须等待获取监视器。
    在您的情况下,您可以使用private static final Object lock = new Object();作为锁定,您将同步

    编辑2:我完全构建了你的代码
    我没有为完成你的所有工作感到骄傲,但无所谓,这就是它。

    package anything.synchronize_stackoverflow_post;
    
    /**
     * @date 21.11.2012
     * @author Thomas Jahoda
     */
    public class ConcurrentMatrixMultiplyingTask implements Runnable {
    
        private int[][] matrixA;
        private int[][] matrixB;
        private int[][] matrixProduct;
        //
        private final ConcurrencyContext context;
    
        public ConcurrentMatrixMultiplyingTask(ConcurrencyContext context, int[][] A, int[][] B, int[][] C) {
            if (context == null) {
                throw new IllegalArgumentException("context can not be null");
            }
            this.context = context;
            this.matrixA = A;
            this.matrixB = B;
            this.matrixProduct = C;
        }
    
        @Override
        public void run() {
            while (true) {
                int row;
                synchronized (context) {
                    if (context.isFullyProcessed()) {
                        break;
                    }
                    row = context.nextRowNum();
                }
                System.out.println(Thread.currentThread().getName() + " is going to process row " + row);
                // i'm not really sure if this matrix algorithm here is right, idk..
                for (int j = 0; j < matrixB[0].length; j++) {
                    for (int k = 0; k < matrixA[0].length; k++) {
                        matrixProduct[row][j] += matrixA[row][k] * matrixB[k][j];
                    }
                }
            }
        }
    
        public static class ConcurrencyContext {
    
            private final int rowCount;
            private int nextRow = 0;
    
            public ConcurrencyContext(int rowCount) {
                this.rowCount = rowCount;
            }
    
            public synchronized int nextRowNum() {
                if (isFullyProcessed()) {
                    throw new IllegalStateException("Already fully processed");
                }
                return nextRow++;
            }
    
            public synchronized boolean isFullyProcessed() {
                return nextRow == rowCount;
            }
        }
    }
    

    ProcessingTask

    package anything.synchronize_stackoverflow_post;
    
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Random;
    import java.util.Scanner;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    /**
     * @date 21.11.2012
     * @author Thomas Jahoda
     */
    public class MatrixMulti {
    
        public static void main(String[] args) {
            int[][] matrixA;
            int[][] matrixB;
            int colA = 0;
            int rowA = 0;
            int colB = 0;
            int rowB = 0;
            Scanner userInput = new Scanner(System.in);
            System.out.println("Please enter the dimensions of matrix A");
    
            do {
                System.out.print("column for matrix A: ");
                colA = userInput.nextInt();
                System.out.println();
            } while (!validDimension(colA));
    
            rowB = colA;
    
            do {
                System.out.print("row for matrix A: ");
                rowA = userInput.nextInt();
                System.out.println();
            } while (!validDimension(rowA));
    
            matrixA = new int[rowA][colA];
    
            System.out.println("Please enter the dimensions of matrix B:");
            do {
                System.out.print("column for matrix B: ");
                colB = userInput.nextInt();
                System.out.println();
            } while (!validDimension(colB));
    
            matrixB = new int[rowB][colB];
    
    
            fillMatrix(matrixA);
            fillMatrix(matrixB);
    
            System.out.println("Would you like to print out matrix A and B? (y/n)");
            String userResponse = userInput.next();
            if (userResponse.equalsIgnoreCase("y")) {
                System.out.println("Matrix A:");
                printBackMatrix(matrixA);
                System.out.println();
                System.out.println("Matrix B:");
                printBackMatrix(matrixB);
                System.out.println();
            }
    
    
            int[][] matrixProduct3 = multMatrixWithThreadsSync(matrixA, matrixB);
    
            String fileName = "test.txt";
            System.out.println("Matrix product is being written to " + fileName);
            try {
                printMatrixToFile(matrixProduct3, fileName);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        private static int[][] multMatrixWithThreadsSync(int[][] matrixA, int[][] matrixB) {
    
            int[][] matrixProduct = new int[matrixA.length][matrixB[0].length];
            int[] matrixProductColumn = new int[matrixA.length];
            //
            ConcurrentMatrixMultiplyingTask.ConcurrencyContext context = new ConcurrentMatrixMultiplyingTask.ConcurrencyContext(matrixProduct.length);
            //
            Runnable task = new ConcurrentMatrixMultiplyingTask(context, matrixA, matrixB, matrixProduct);
            Thread[] workers = new Thread[5];
            for (int i = 0; i < workers.length; i++) {
                workers[i] = new Thread(task, "Worker-"+i);
            }
            for (int i = 0; i < workers.length; i++) {
                Thread worker = workers[i];
                worker.start();
            }
            for (int i = 0; i < workers.length; i++) {
                Thread worker = workers[i];
                try {
                    worker.join();
                } catch (InterruptedException ex) {
                    Logger.getLogger(MatrixMulti.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            return matrixProduct;
        }
    
        private static void printMatrixToFile(int[][] matrix, String fileName) throws IOException {
            PrintWriter userOutput = new PrintWriter(new FileWriter(fileName));
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[0].length; j++) {
                    userOutput.print(matrix[i][j] + " ");
                }
                userOutput.println();
            }
            userOutput.close();
    
        }
    
        private static void printBackMatrix(int[][] matrix) {
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[0].length; j++) {
                    System.out.print(matrix[i][j] + " ");
                }
                System.out.println();
            }
        }
    
        private static void fillMatrix(int[][] matrix) {
            Random rand = new Random();
    
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[0].length; j++) {
                    matrix[i][j] = rand.nextInt(100) + 1;
                }
            }
    
        }
    
        public static boolean validDimension(int dim) {
            if (dim <= 0 || dim > 1000) {
                System.err.println("Dimension value entered is not valid");
                return false;
            }
            return true;
    
        }
    }
    

答案 1 :(得分:1)

要解决您的问题,您需要定义什么是“工作单位”。这个“工作单元”(或任务)是每个线程将要执行的。在定义之后,您可以推断出这个工作单元需要做什么工作。

在矩阵乘法的情况下,自然工作单元是所得矩阵的每个单元格。因此,给定矩阵A [i,j]和B [j,k],您的计算可以集中于每个{{1的向量A.row(x)(点)B.column(y)的点积。 }}

下一步是代表每项任务。将任务“提供”到线程的理想结构是队列。 java.util.concurrent.BlockingQueue就是这样一个例子,同步工作在幕后完成。鉴于您被要求“手动”推理同步,您可以使用另一个容器,如List(甚至是数组)。您的结构将包含定义结果矩阵的每个单元格。 可能是这样的:

(0<=x<i,0<=y<k)

现在,您需要一个给定Cell和Matrices A和B的任务,可以计算该单元格的值。这是您的工作单元,因此在线程的上下文中运行的是什么。在这里,您还需要决定是否要放置结果。在java中你可以使用future并在线程的上下文之外组装你的矩阵,但是为了简单起见,我将共享一个可以保存结果的数组。 (根据定义,不会有任何碰撞)

class Cell;  // int x, int y, getters, setters, ...
// build the structure that contains the work to be shared
List<Cell> cells = new LinkedList<Cell>();
for (int i=0;i<a.rows;i++) {
   for (int j=0;j<b.columns;j++) {
       cells.add(new Cell(i,j)); // represent the cells of my result matrix
   }
}

现在你差不多完成了。您仍然需要做的唯一事情是创建线程,使用class DotProduct implements Runnable { int[][] a; int[][] b; int[][] result; List<Cell> cells; public DotProduct(int[][] a, int[][] b, int[][]result, List<Cell> cells) { ... } public void run() { while(true) { Cell cell = null; synchronized(cells) { // here, we ensure exclusive access to the shared mutable structure if (cells.isEmpty()) return; // when there're no more cells, we are done. Cell cell = cells.get(0); // get the first cell not calculated yet cells.remove(cell); // remove it, so nobody else will work on it } int x = cell.getX(); int y = cell.getY(); z = a.row(x) (dot) b.column(y); synchronized (result) { result[x][y] = z; } } } 任务“提供它们”并等待它们完成。 请注意,我在DotProduct上同步以更新结果矩阵。虽然根据定义,并不存在并发访问同一个单元的可能性(因为每个线程都在不同的单元上工作),但您需要通过显式同步对象来确保结果“安全地”发布到其他线程。这也可以通过声明result result来完成,但我不确定您是否已经覆盖了这一点。

希望这有助于理解如何处理并发问题。

答案 2 :(得分:0)

您使用所有频谱的同步原语:信号量,锁定,同步。最好从同步开始,学习东西。您实际需要的是一个资源,它指示要处理的下一行/列(如果有)。所有线程使用synchronized块访问它,读取下一行/列,将行/列移动到下一个单元格,退出块,然后处理获得的行/列。

如果满足矩阵的结束,工作线程就会退出。主线程等待所有工作线程使用Thread.join()退出。

答案 3 :(得分:0)

你真的误解了上一个问题的答案。需要在线程之间共享rowToWork。一个线程应该在构造时调用一个方法来获取它的初始值。您需要了解您的Critical Section是给定线程的下一行的归属。