[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[jfriends:00497] Re: Thread の一時停止と再開について





吉田です。

At 31 Oct 2001 16:54:29 +0900 Titta2@xxxxxxxxxxxx writes
>
>信乃です。
>
>>Threadクラスのインスタンス(オブジェクト)とスレッドを分けて考えてください。 
>すみません。同じものとして捉えてました。
>
>>thはThreadクラスのオブジェクトで、thisはStop_Resumeクラスのオブジェクトを 
>>指しています。 
>それでsynchronized(object)をthisにするとおかしくなってしまうんですね・・・
>ようやく合点がいきました。
>
>何度もありがとうございました。
>大変勉強になりました。

私は、これを読んだときに、notify ではなく、 notifyAll を使う方が、いいので
はないかと思いました。

下記のJavaプログラム引数に、10 500 を与えてみると、
notifyでは、期待したスレッドが再開されない例外的なケースをシミュレート出来ます。

package test;

public class NotifyTest extends Thread {
    public static Object mutex = new Object();

    public static int notifyId = 0;

    public static void main(String[] args) {
        int maxThread = 10;
        int sleepTime = 500;

        try {
            maxThread = Integer.parseInt(args[0]);
        }catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("args[0]でスレッドの数を指定出来ます");
        }catch(NumberFormatException e) {
            System.out.println("args[0]でスレッドの数を指定出来ます");
            e.printStackTrace();
        }
        try {
            sleepTime = Integer.parseInt(args[1]);
        }catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("args[1]で処理のウェイトを指定出来ます");
        }catch(NumberFormatException e) {
            System.out.println("args[1]で処理のウェイトを指定出来ます");
            e.printStackTrace();
        }

        NotifyTest[] states = new NotifyTest[maxThread];
        for(int i = 0; i < maxThread; ++ i) {
            states[i] = new NotifyTest(i);
            states[i].start();
        }

        try {
            System.out.println("1秒後にテストを開始します");
            Thread.sleep(1000);
        }catch(InterruptedException e) {
            e.printStackTrace();
            return;
        }

        notifyId = maxThread - 1;
        int term = 0;
        for(int j = 0; j < 30; ++ j) {
            try {
                if(sleepTime > 0) Thread.sleep(sleepTime);
            }catch(InterruptedException e) {
                e.printStackTrace();
                return;
            }
            try {
                synchronized(mutex) {
                    System.out.println();
                    while(notifyId > 0 && states[notifyId].isHappy) {
                        notifyId --;
                    }
                    System.out.print("notify" + notifyId + " ");
                    mutex.notify();
                }
            }catch(IllegalStateException e) {
                e.printStackTrace();
            }
        }
        System.out.println();

        while(states[0].isHappy == false) {
        //for(int j = 0; j < 30; ++ j) {
            try {
                if(sleepTime > 0) Thread.sleep(sleepTime);
            }catch(InterruptedException e) {
                e.printStackTrace();
                return;
            }
            try {
                synchronized(mutex) {
                    System.out.println();
                    while(notifyId > 0 && states[notifyId].isHappy) {
                        notifyId --;
                    }
                    System.out.print("notifyAll" + notifyId + " ");
                    mutex.notifyAll();
                }
            }catch(IllegalStateException e) {
                e.printStackTrace();
            }
        }
        System.out.println();
    }

    private final int id;

    public NotifyTest(int i) {
        id = i;
        setDaemon(true);
        isHappy = false;
    }

    public void run() {
        synchronized(mutex) {
            try {
                while(true) {
                    mutex.wait();
                    if(id == notifyId) {
                        System.out.print(":" + id + "=wanted");
                        isHappy = true;
                        break;
                    }
                    System.out.print(":" + id);
                }
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isHappy;
}

問題がもうひとつありまして、100 0 を与えてみると、notifyAllでも、目的の
スレッドが再開されない事がある様なので、notifyAll 後に、
前後関係によっては、Thread.yield() も必要なの物なのでしょうか。

#ひょっとして、FAQなのでしょうか。
#もしFAQで無いなら、なんとなくJavaのバグに見えてしまう。
#GUIなら問題はなさそうだけど・・

あるいは、isAliveによる検査方法がまずい?

sleepTime >= 1 の出力
134 行

notifyAll11 :4:9:1:0:6:11=wanted:7:8:10:2:3:5
notifyAll10 :4:9:1:0:6:7:8:10=wanted:2:3:5
notifyAll9 :4:9=wanted:1:0:6:7:8:2:3:5
notifyAll8 :4:1:0:6:7:8=wanted:2:3:5
notifyAll7 :4:1:0:6:7=wanted:2:3:5
notifyAll6 :4:1:0:6=wanted:2:3:5
notifyAll5 :4:1:0:2:3:5=wanted
notifyAll4 :4=wanted:1:0:2:3
notifyAll3 :1:0:2:3=wanted

sleepTime == 0 の出力 (sleepなし) 
4554行

notifyAll10 
notifyAll10 
notifyAll10 :0:5:3:4:6:7:8:10=wanted:2:1:9
notifyAll9 :0:5:3:4:6:7:8:2:1:9=wanted
notifyAll8 :0:5:3:4:6:7:8=wanted:2:1
notifyAll7 :0:5:3:4:6:7=wanted:2:1
notifyAll6 :0:5:3:4:6=wanted:2:1
notifyAll5 :0:5=wanted:3:4
notifyAll4 :2:1
notifyAll4 :0:3:4=wanted
notifyAll3 :2:1
notifyAll3 :0:3=wanted
notifyAll2 :2=wanted:1
notifyAll1 :0
notifyAll1 :1=wanted
notifyAll0 :0=wanted
notifyAll0 

notifyAllの場合と違い、notifyの場合には抜けがありません。
なので、キューイベントは、待機スレッド、キュー毎のObject、wait(), notify()だけで
ずいぶんスマートに実装できるのですね。

最後の2行は、whileからsynchronizedの間でダブルチェックしていないからですが、
他の重複部分がどこを通過しているのでしょうか。
synchronized(mutex) {
    if(states[0].isHappy)
        break;
}

きっと、パフォーマンスの関係で、notifyAllは、全てのスレッドに順番をゆずる前に、
自分自身にも、順番がまわって来るのだろうという事で納得します。

今作成している、アプリケーションで、wait, notify,が肝になっていますので、
いろいろ考えてしまいました。もし、FAQの様な物や、経験などございましたら、
いろいろお聞かせ願えれば幸いです。


-- 
S.Yoshida <vzy03312@xxxxxxxxxxxxxxxxx>

------------------------------------------------------------------------
         ★ こっちの流行語大賞はどんなの?          
  http://www.infoseek.co.jp/Keyword?pg=nranking_top_if.html&svx=971122