簡體   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