1

2つのスレッドを持つThreadManagerがあります。1つはGUI関連のリクエスト用で、もう1つは測定関連のリクエスト用です。は、リクエストのキューを実行およびチェックしています。存在する場合は、リクエストを処理しています。ThreadManager.addGuiRequest(eGuiRequest)staticメソッドとメソッドを使用して、いつでもリクエストを追加できますThreadManager.addMeasRequest(eMeasRequest)。ここで、これらの両方を初期化する必要があります。これはINIT、対応するキューに要求を追加することによって行われます。ただし、測定の初期化は、GUIがすでに初期化されているという事実に依存しています。を使用してこれを解決しようとしましたが、機能さwait()/notify()せることができません。

これがSSCCEです。起動時に、両方のキューにINIT要求が追加されてから、開始されます。測定の初期化は、GUIがまだ初期化されていないことを検出し、aを実行しwait()ます。GUIが初期化されます(5秒間スリープすることでシミュレートされます)。これはすべて正常に機能します。

GUIが初期化された後、測定スレッドをウェイクアップしようとしますが、測定スレッドはウェイクアップしません...私はこの記事wait()/notify()に基づいてコードを作成しました。ここで何が問題になっていますか?

import java.util.LinkedList;
import java.util.NoSuchElementException;

public class ThreadManager {    
    public static void main(String[] args) {
        new ThreadManager();
        ThreadManager.addMeasRequest(eMeasRequest.OTHER_STUFF);
    }

    public enum eGuiRequest { INIT, OTHER_STUFF; }
    public enum eMeasRequest { INIT, OTHER_STUFF; }

    private static LinkedList<eGuiRequest> guiQueue = new LinkedList<eGuiRequest>();
    private static LinkedList<eMeasRequest> measQueue = new LinkedList<eMeasRequest>();
    private static Thread guiThread, measThread;
    protected boolean initialized = false;

    public ThreadManager() {
        final int waitMs = 200;    
        guiThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        if (guiQueue.isEmpty()) sleepMs(waitMs);
                        else {
                            eGuiRequest req = guiQueue.getFirst();
                            processGuiRequest(req);
                            guiQueue.removeFirst();
                        }
                    } catch (NoSuchElementException e) {}
                }
            }

            private void processGuiRequest(eGuiRequest req) {
                System.out.println("T: " + "Processing Gui request: " + req);
                switch (req) {
                case INIT:
                    // do some initializiation here - replaced by a wait:
                    sleepMs(5000);
                    System.out.println("I: " + "guiThread finished, waking up measThread");
                    synchronized (measThread) {
                        initialized = true;
                        measThread.notify();
                    }
                    break;
                case OTHER_STUFF:
                    // do other stuff
                    break;
                }
            }
        });
        measThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        if (measQueue.isEmpty()) sleepMs(waitMs);
                        else {
                            eMeasRequest req = measQueue.getFirst();
                            processMeasurementRequest(req);
                            measQueue.removeFirst();
                        }
                    } catch (NoSuchElementException e) {}
                }
            }

            private void processMeasurementRequest(eMeasRequest req) {
                if (req == eMeasRequest.INIT) { // if init, wait until GUI is initialized
                    synchronized (this) {
                        while (!initialized) {
                            System.out.println("I: " + "measThread waits for guiThread to finish initializiation");
                            try {
                                wait();
                            } catch (Exception e) {}
                            System.out.println("I: " + "measThread awakes");
                        }
                    }
                }
                System.out.println("T: " + "Processing Measurement request: " + req);
                // process request here:
                sleepMs(5000);
            }
        });

        addGuiRequest(eGuiRequest.INIT);
        addMeasRequest(eMeasRequest.INIT);

        guiThread.start();
        measThread.start();
    }

    public static void sleepMs(int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException ee) {}
    }

    public static void addGuiRequest(eGuiRequest req) {
        guiQueue.add(req);
    }

    public static void addMeasRequest(eMeasRequest req) {
        measQueue.add(req);
    }
}
4

3 に答える 3

3

GUIスレッドはnotify()measThreadThreadタイプの)をprocessMeasurementRequest()呼び出し、メソッドはwait()を呼び出します。thisこれは、によって使用されるRunnableインスタンスmeasThreadです。

待機して通知するために、両方のスレッドで共有されている特定のオブジェクトを使用することをお勧めします。

private static final Object GUI_INITIALIZATION_MONITOR = new Object();

また、LinkedListを使用してリクエスト間で任意の時間をスリープ状態にする代わりに、BlockingQueueを使用します。これにより、消費スレッドがリクエストを受信するとすぐに処理できるようになり、スリープ状態からの不要なウェイクアップを回避できます。

また、低レベルの待機/通知の代わりに、1に初期化されたCountDownLatchを使用できます。GUIスレッドはcountDown()初期化時にラッチし、測定スレッドはawait()GUIスレッドが呼び出すまでラッチしますcountDown()。これにより、複雑な同期と通知が、より高レベルで十分にテストされたオブジェクトに委任されます。

于 2012-06-20T09:16:01.410 に答える
1

主な問題は、を呼び出すことですが、匿名クラスで呼び出されますnotify()。これを修正する最も簡単な方法は、同期用の特別なオブジェクトを作成することです。たとえば、次のフィールドを作成します。measThreadwait()

private static final Object LOCK = new Object();

次に、このオブジェクトを使用して同期ブロックを記述し、次のようにそのメソッドを呼び出します。

synchronized (LOCK) {
    while (!initialized) LOCK.wait();
}

また、このコードは、異なるスレッドからアクセスされたフィールドに対して同期をまったく使用していないことを言わなければなりません。つまり、いつでも壊れることがあります。両方のキューは、自分で作成したスレッドの外部からアクセスされます。つまり、常にロックを保持したままアクセスするか、組み込みの同期リストを使用してスレッドセーフにすることができます。

quiQueue = Collections.synchronizedList(new LinkedList<eGuiRequest>());

initialized同期されたブロックからアクセスされますが、現在、それらは異なるロックで同期しています(この問題については、回答の冒頭で説明しました)。この問題を修正すると、initializedも正常に機能します。

于 2012-06-20T09:34:01.623 に答える
1

起動時に測定にinitリクエストを送信しないでください。init gui requestの実行後、processGuiRequest()から送信されます。その後、待機/通知する必要はありません。

于 2012-06-20T09:40:35.710 に答える