0

2 つのスレッドを開始するコード スニペットを作成しました。1 つのスレッドはすべての奇数を出力し、別のスレッドはすべての偶数を出力します。固有のロックとスレッド通信コマンドを組み合わせて使用​​し、2 つのスレッドの適切なインターリーブを実現しました。ここに私のコードがあります、

public class threadEvenOdd implements Runnable
{
    static Boolean isOdd=true;
    int count = 10;
    Boolean value;
    static int c=1;
    static Object lock = new Object();

    threadEvenOdd(Boolean temp)
    {
        value = temp;
    }
    public void run()
    {
        if(value)
        {
            printOdd(count);
        }
        if(!value)
        {
            printEven(count);
        }
    }
    void printOdd(int count)
    {
        try
        {
            for(int i=0;i<count/2;i++)
            {
                //System.out.println("odd enters lock");
                synchronized(lock)
                {
                    if(!isOdd)
                    {
                        //System.out.println("odd in barrier");
                        lock.wait();
                    }
                    System.out.println(c);
                    c++;
                    isOdd = false;
                    //System.out.println("odd notifies");
                    lock.notify();
                }
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
    void printEven(int count)
    {
        try
        {
            for(int i=0;i<count/2;i++)
            {
                //System.out.println("even enters lock");
                synchronized(lock)
                {
                    if(isOdd)
                    {
                        //System.out.println("even in barrier");
                        lock.wait();
                    }
                    System.out.println(c);
                    c++;
                    isOdd = true;
                    //System.out.println("even notifies");
                    lock.notify();
                }
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
    public static void main (String args[])
    {
        threadEvenOdd th1 = new threadEvenOdd(true);
        threadEvenOdd th2 = new threadEvenOdd(false);
        Thread t1 = new Thread(th1);
        t1.setName("odd");
        Thread t2 = new Thread(th2);
        t2.setName("even");
        //System.out.println(t1.getName() + " starts");
        t1.start();
        //System.out.println(t2.getName() + " starts");
        t2.start();
    }
}

ここに私の質問があります:

  1. 奇数スレッドは printOdd() 関数で実行され、偶数スレッドは printEven() 関数で実行されます。両方のスレッドに 1 つの固有ロックを使用しています。同じロックを使用するため、2 つのスレッドがそれぞれの同期ブロックに同時に存在する方法がわかりません。

  2. コードからスレッド通信ステートメント (通知、待機) を削除しましたが、それでも目的の出力が得られました。私のコードが実際にスレッド通信ステートメントを必要とするかどうか疑問に思っています。

  3. 私はまだ自分のコードを理解するのに苦労しているので、マルチスレッドの概念の理解に取り組む必要があると思います:p私が使用したマルチスレッドの概念のみを使用してこれを行うより良い方法があるかどうか誰か説明できますか?

4

1 に答える 1

1
  • 各スレッドには、コードを介した独自の実行パスがあります。2 つのスレッドがまったく同じコードを実行する場合でも、コードを介してコードを実行することで、2 つの異なる実行ポイントが存在します。スレッドが同期ステートメントに到達すると、ロックが使用可能になるのを待ちます。同じロックによって保護されている同期ブロック内に他のスレッドが存在しない場合にのみ、同期ブロックに入ります。

  • 通知/待機ステートメントを削除しても、同じ出力が得られ続けますが、偶然である可能性があります。countフィールドの比較的大きな値でこれを試しましたか?

  • このプログラムが生成する出力を指定していないため、現時点でこの質問に答えるのは難しいです。「1,3,5,7,9,2,4,6,8」は有効な出力ですか? 「1,3,2,4,6,5,7,9,8」ですか?または、「1,2,3,4,5,6,7,8,9」が唯一の有効な出力ですか? とはいえ、ここにいくつかの簡単なポイントがあります。

  • notify の代わりに notifyAll() を使用します

  • スレッド間で共有される状態を最小限に抑えます。この場合、 と の両方を共有isOddcます。前者は、 を介して後者から計算できることに注意してくださいc % 2 == 1。したがって、スレッドを共有データの一部として維持するのではなく、奇妙な計算を行うことができます。

  • 静的フィールドを介して共有する代わりに、(インスタンス フィールドを使用して) オブジェクトを作成し、このオブジェクトを各スレッドのコンストラクターに渡します。その後、オブジェクト自体をロックとして使用できます。

これは次のようになります。

class SharedData {
    int c;
    boolean isOdd;
} 

class ThreadEvenOdd {
    SharedData sharedData;

    public ThreadEvenOdd(SharedData sd) { this.sharedData = sd }

    // ...

    void printOdd(int count) {
        try {
            for(int i=0;i<count/2;i++) {
                synchronized(sharedData) {
                    if(!sharedData.isOdd) { ... }
                    System.out.println(sharedData.c);
                    sharedData.c++;
                    sharedData.isOdd = false;
                    lock.notify();
                }
            }
        }
        catch(Exception e) {
            System.out.println(e);
        }
    }
}   

それについての良い点は、sharedData で実際のメソッドの定義を開始できることです (たとえば、値に基づいて適切な値を増やしcて設定するメソッドなど)。これにより、スレッド クラスのコードがさらに簡素化され、同期/通知がデータの処理にあまりインターリーブされないため、コードが読みやすくなり、エラーが発生しにくくなります。isOddc

于 2013-07-15T05:41:09.440 に答える