3

たとえば、以下のコードのように、何らかの用途のためにスレッドを再起動したいと考えています。

class Ex13 implements Runnable {
    int i = 0;

    public void run() {
        System.out.println("Running " + ++i);
    }

    public static void main(String[] args) throws Exception {
        Thread th1 = new Thread(new Ex13(), "th1");
        th1.start();
            //th1.join() 
        Thread th2 = new Thread(th1);
        th2.start();
    }
}

上記のプログラムを実行しているときに、Running 1 Running 2 という出力が得られたり、Running 1 しか得られないことがあります。数回実行した後、出力として Running 1 しか得られません。私はこの行動に完全に驚いています。誰でもこれを理解するのを手伝ってくれますか? join() を置くと、実行中の 1 しか得られません。

4

4 に答える 4

2

Threadではなく、インスタンスを再利用しますRunnable。スレッドはそのrun()メソッドを

public void run() {
    if (target != null) {
        target.run();
    }
}

target はRunnable、コンストラクターに渡すものです。それに加えて、ThreadVMexit()によって呼び出されるメソッドがあり、このメソッドは target を null に設定します (理由はこのバグです)。したがって、最初のスレッドが実行を終了する可能性がある場合、そのrun()メソッドはほとんど空です。を追加th1.join()するとそれが証明されます。

何らかの状態を保持したい場合は、.Runnableではなく、インスタンスへの参照を保持する必要がありますThread。このrun()方法は変更されません。

于 2012-06-30T14:49:56.517 に答える
2

なぜこれが必要なのかわかりませんが、(ただし、このコードでは、th1 が常に th2 の前に実行されるとは限らないことに注意してください):

public static class Ex13 implements Runnable {
    AtomicInteger i = new AtomicInteger(0);
    CountDownLatch latch;
    Ex13(CountDownLatch latch) {
        this.latch = latch;
    }
    public void run() {
        System.out.println("Running " + i.incrementAndGet());
        latch.countDown();
    }
}

public static void main(String[] args) throws Exception {
    CountDownLatch latch = new CountDownLatch(2);
    Ex13 r = new Ex13(latch);
    Thread th1 = new Thread(r, "th1");
    th1.start();
    Thread th2 = new Thread(r);
    th2.start();
    latch.await(); // wait until both theads are executed
    System.out.println("Done");
}
于 2012-06-30T09:30:39.447 に答える
0

i のインクリメントを同期させたい、つまり

public class Ex13 implements Runnable {
    int i=0;
    public void run() {
        System.out.println("Running "+ increment());
    }
    private synchronized int increment() {
        return ++i;
    }
}

Java チュートリアルには、非常によく似たシナリオが与えられた場合の非常に優れた説明があります。問題は、変数のインクリメントがアトミック操作ではないことです。各スレッドは、新しい値に設定する前に i の現在の状態を読み取る必要があります。一度に 1 つのスレッドに変数をインクリメントするようにアクセスを制限すると、一貫した動作が保証されます。

于 2012-06-30T09:22:13.363 に答える
0

System.out.println で何が起こっているかを確認するには、スレッド名を出力することもできます。

Thread t = Thread.currentThread();
String name = t.getName();
System.out.println("name=" + name);

同じ実行可能なオブジェクトで 2 つのスレッドを呼び出していることがわかります。そのため、どちらも同じ "i" 変数を使用します。Running 1 Running 2 を取得するには、"i" を同期する必要があります。

于 2012-06-30T10:02:16.640 に答える