4

我整晚都在研究这个,还没有找到解决方案,所以如果有人能帮助我,我会非常感激!我可能遗漏了一些非常明显的东西。这是一个理解同步的分配,我们在之前的分配中使用线程来乘以 2 个矩阵。在之前的分配中,每个线程乘以一行,因此线程数与行数一样多。

在这个分配中,我们只应该使用 5 个线程——所有线程都应该从一行/列开始,一旦线程完成,它应该使用同步选择下一个可用的行/列,所以现在两个线程将最终执行同一列。

这个问题帮助我找到了正确的方向,但我必须在实施中做错了,因为到目前为止我只得到了程序:

  1. 只做前 5 行——5 个线程执行一次,每个线程计算一行或
  2. 我添加了一个循环(现在在我的代码中被注释掉了),所以线程会继续执行,但是当我这样做时,只有第一个线程做任何工作。

这是我的主要方法和几个辅助方法的课程:

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;

}
}

这是我的可运行类:

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

4 回答 4

3
  1. 您没有在资源上同步,您需要共享一个锁对象(在静态上下文中或通过构造函数)
  2. 当你甚至不让它们同步工作时,我真的无法弄清楚你的程序中应该同步什么......你启动一个线程并直接等待他停止。我认为您必须首先启动它们,然后在另一个循环中调用每个线程上的连接。

另外,我不太确定您的线程应该单独解决什么问题,我认为它们都可以解决整个产品矩阵。您需要共享一个变量,用于识别您同步访问的已处理行。

我可以修复您的代码,但我希望您自己完成这项工作,因为这是一项了解线程并发性的任务。

编辑:同步的解释:
同步将对象作为锁,只有一个线程可以为它保存监视器。当有锁的监视器时,线程可以处理该块,如果没有,他必须等待获得监视器。
在您的情况下,您可以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;
        }
    }
}

和处理任务

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;

    }
}
于 2012-11-21T14:41:09.647 回答
1

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

在矩阵乘法的情况下,工作的自然单位是结果矩阵的每个单元。因此,给定矩阵 A[i,j] 和 B[j,k],您的计算可以集中在向量 A.row(x) (dot) B.column(y) 的点积上(0<=x<i,0<=y<k)

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

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
   }
}

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

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 volatile但我不确定你是否已经涵盖了这一点。

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

于 2012-11-21T18:43:56.480 回答
0

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

如果满足矩阵的结尾,则工作线程简单地退出。主线程使用 Thread.join() 等待所有工作线程退出。

于 2012-11-21T14:44:23.520 回答
0

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

于 2012-11-21T15:22:52.353 回答