1

スレッドを処理するさまざまな方法の影響を示すデモ プログラムを開発しています。私はchar配列を取り、各charをStringBuilderに送信する名前を作成するという単純なタスクを使用しています.StringBuilderはNameBuilderと呼ばれる実行可能オブジェクトを呼び出します。これは、同期されたメソッドを持つletterAdderクラスと静的StringBuilder変数を使用して名前をそのまま保持します構築されました。この構造は、Bruce Eckel 著「Thinking in Java」の WaxoMatic の例に大まかに基づいています。

notifyAll() メソッドに使用しているコードは次のとおりです。

import java.util.List;
import java.util.concurrent.*;

class LetterAdder{
    public static StringBuilder currName = new StringBuilder();
    public static boolean busyAdding = false;

    public synchronized void addLetterSynchWithNotifyAll(char letter) throws InterruptedException{
        while (busyAdding){
            System.out.println("Letter " +letter+" has to wait");
            wait();
        }
        busyAdding=true;
        System.out.println("About to add " + letter + " - ");
        currName.append(letter);
        System.out.println("name is now " + currName);
        busyAdding=false;
    }

    public synchronized void doNotifyAll(char letter) throws InterruptedException{
        System.out.print(letter + " is notifying all");
        notifyAll();
    }
}

class NameBuilder implements Runnable{
    private LetterAdder adder = new LetterAdder();
    private char nextLetter;

    public NameBuilder(char nextLetter){
        adder = new LetterAdder();
        this.nextLetter=nextLetter;
    }

    public void run(){
        try{
            adder.addLetterSynchWithNotifyAll(nextLetter);
                adder.doNotifyAll(nextLetter);
        } catch (InterruptedException e) {
            System.out.println("Letter "+nextLetter+" interrupted!");
        }

        //tie up thread for specified time to ensure other threads go to wait 
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch(Exception e){}
    }
}

public class NotifyDemo {
    public static void main(String[] args) {
        char[] chars = {'M', 'a','r','t','i','n'};
        ExecutorService pool = Executors.newCachedThreadPool();

        for (int i=0; i<chars.length; i++){
            NameBuilder builder = new NameBuilder(chars[i]);
            pool.execute(builder);
        }

        System.out.println("\nWill shutdown thread pool in 20 seconds");
        try {
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {}
        System.out.println("\nAbout to shutdown thread pool ");
        List<Runnable> tasks = pool.shutdownNow();
        if (!tasks.isEmpty()){
            for (Runnable r: tasks){
                System.out.println("Uncompleted task: "+r.toString());
            }
        }
    }
}


/** Output
About to add M - 
Letter a has to wait
name is now M
Letter r has to wait
M is notifying allLetter t has to wait
About to add i - 

Will shutdown thread pool in 20 seconds
Letter n has to wait
name is now Mi
i is notifying all
About to shutdown thread pool 
Letter a interrupted!
Letter t interrupted!
Letter n interrupted!
Letter r interrupted!
*/

出力に出力されたステートメントからわかるように、文字が busyAdding モニターに到達して待機メッセージを出力するとブロックされますが、notifAlly() が送信されても​​待機状態から抜け出すことはありません。

また、中断されたスレッドが shutdownNow() ステートメントによって返されるリストに表示されることを期待しますが、リストは空です。

私は明らかな何かが欠けていると感じています.誰かが私がここで間違っていることを見つけることができますか?

4

1 に答える 1

1

異なる adder 間で busyAdding を共有していますが、異なる adder ごとにロックして通知します。

これは、次のことが起こり得ることを意味します。'O'、'p'、's' の 3 つの加算器を作成するとします。最初の加算器はaddLetterSynchWithNotifyAllに入り、ガードを通過してビジー信号を true に設定します。ここで、次の 2 人のランナーが入り、ガードにスタックします。最初のスレッドが存在すると、最初のランナーで待機しているすべてのスレッドに通知します (現時点ではありません)。オブジェクト間で信号を共有したい。

addLetterSynchWithNotifyAllを静的にすることもできます。これは、同期がクラスレベルになることを意味します。次に、インスタンスではなくクラスになるように待機を変更する必要があります。または、同期と待機に使用される共通のロック オブジェクトを作成できます。

メソッドを静的にすると、他のスレッドは同じオブジェクトで同期されるため、メソッドに入ることがなくなり、 busyAddingによる保護が不要になることに注意してください。

于 2012-11-21T22:35:02.600 に答える