繁体   English   中英

如何在没有工作线程和控制器的情况下在一类中同步多线程应用程序?

[英]How to synchronize multithreaded app in one class, without worker threads and controller?

我有多个线程可以大致计算给定数组的总和。 我不能使用单独的工作线程和控制器,而是必须将整个程序编写在一个类中。 程序的重点是打印计算结果,以便线程ID 0是第一个打印结果的线程,然后是线程ID 1,然后是线程ID 2,依此类推。

我在该类中编写了两个同步方法: finished -在特定线程完成计算finished通知所有线程,而waitForThread等待ID小于其自身线程的线程还没有完成。

因为不能使用单独的控制器类,所以我决定创建一个静态数组doneArr ,其中每个单元格都代表一个线程ID,并且一旦线程完成其工作,它将更新doneArr的相应单元格。 这样, waitForThread知道何时等待。

我似乎无法正常工作,能否请您指出正确的方向?

编辑:可以通过使用join()轻松解决问题,但是我正在寻找使用同步的解决方案。

这是代码:

public class WorkThread extends Thread {
    private int[] vec;
    private int id;
    private int result;
    private static int[] doneArr;
    private static int progress = 0;

    public WorkThread(int[] vec, int id) {
        this.vec = vec;
        this.id = id;
        doneArr = new int[vec.length];
        for(int i = 0; i < doneArr.length; i++) {
            doneArr[i] = -1; //default value
        }
    }
    private boolean finishedUpToMe() {
        for(int i = 0; i < id; i++) {
            if(doneArr[i] == -1)
                return false;
        }
        return true;
    }
    private synchronized void waitForThread() throws InterruptedException {
        while(!finishedUpToMe() && id > 0)
            this.wait();
        progress++;
    }
    private synchronized void finished() throws IllegalMonitorStateException {
        this.notifyAll();
    }
    public int process(int[] vec, int id) {
        int result = 0;
        System.out.format("id = %d\n", this.id);
        for(int i = 0; i < vec.length; i++) {
            vec[i] = vec[i] + 1;
            result = result + vec[i];
        }
        return result;
    }
    public void run() {
        try {
            this.waitForThread();
        }catch (InterruptedException e) {
            System.out.println("waitForThread exception");
        }
        result = process(vec, id);
        doneArr[id] = id;
        System.out.format("id = %d, result = %d\n", id, result);
        try{
            this.finished();
        }catch (IllegalMonitorStateException e) {
            System.out.format("finished exception\n");
        }
    }
    public static void main(String[] args) {
        int[] vec = {1,2,3,4};
        WorkThread[] workers = new WorkThread[3];
        for(int i = 0; i < workers.length; i++) {
            workers[i] = new WorkThread(vec, i);
        }
        for(int i = 0; i < workers.length; i++) {
            workers[i].start();
        }
        System.out.format("main\n");
    }
}

您反复初始化相同的静态成员,尽管这样做无害,但这是一个相当糟糕的做法。 而是使用静态初始化块来初始化静态变量(而不是构造函数)。

private static final int[] doneArr;
static {
    for(...) {...}
}

但是,当然,在这种情况下,您会遇到其长度可变的问题。 我还注意到doneArr包含整数,但是您只是将其用作标志(即-1或!-1)。 因此,请改用Map<Integer, Boolean>

private static final Map<Integer, Boolean> threadIsDone = new HashMap<>();

public WorkThread(int[] vec, int id) {
    threadIsDone.put(id, false);
    ...
}

一旦执行了此操作,虽然在这种特殊情况下并非绝对必要,但在检查时防止丢失值仍然是一种很好的做法。

private boolean finishedUpToMe() {
    for(int i = 0; i < id; i++) {
        final Boolean threadDone = threadIsDone.get(i);
        if (threadDone != null && !threadDone)
            return false;
    }
    return true;
}

至于您的线程问题,似乎您使用了声明为已同步的方法。 这对于共享对象上的方法非常有用,但是实际上并不是您所拥有的-您有多个唯一的工作程序对象。 因此,您实际上并没有在同一个对象上进行同步。 这意味着:

  • JVM不必使写入对其他线程可见。
  • 如果确实碰巧看到写入,则说明您处于竞争状态。
  • notifyAll()的调用实际上并没有完成任何事情。

而是使用声明为static final的锁对象。

private static final Object lockObj = new Object();

private void waitForThread() {
    synchronized(lockObj) {...}
}

private void finished() {
    synchronized(lockObj) {...}
}

编辑:其他想法

  • 如果声明的变量靠近实际使用的位置(即,紧接在相应方法的上方),则代码的可读性更高。
  • 您的process(...)方法是一个实例方法,但是将对它已经可以访问的实例变量的引用作为参数。
  • 如果出于各种原因您不打算修改变量,则应将变量声明为final

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM