0

スレッドを正しく理解しているかどうかわからない場合、次の例で誰かが私が正しいか間違っているかを教えてもらえますか?

class Task {
String taskName;
private Thread thread;
boolean isFinished;

public Task(String name){
    taskName = name;
}

public void createTask(final Runnable r) {
    thread = new Thread(r){
        public void run(){
            if(r != null) {
                r.run();
                isFinished = true;
            }
        }
    };
    thread.start();
}
}

私が実際にアプリで行っているのは、をに設定しisFinished、trueの場合はtrueいつでも何かを実行するオブザーバーを持っていることです。パラメータとして渡すIのすべてのコードが実際に終了する前に、trueに設定されているのではisFinishedないかと思います。isFinishedRunnable

runメソッドは、渡したコードを別のスレッドに入れて、そのコードを非同期で実行することを想定していませんか?

4

7 に答える 7

2

閉じますが、新しいスレッドには実行可能な実行可能オブジェクトがすでに与えられています。r.run()メソッドを実行してからisFinishedを設定するラッパーを本当に与えたいと思います。

変化する:

public void createTask(final Runnable r) {
    thread = new Thread(r){
        public void run(){
            if(r != null) {
                r.run();
                isFinished = true;
            }
        }
    };
    thread.start();
}

public void createTask(final Runnable r) {
    thread = new Thread( new Runnable {
        public void run(){
            if(r != null) {
                r.run();
                isFinished = true;
            }
        }
    });
    thread.start();
}

isFinishedのスレッドの危険性を指摘しなければ、私は失望するでしょう。同期を追加しないと、スレッドが終了したときに通知される保証はありません。追加することをお勧めします:

public synchronized boolean getIsFinished()
{
    return isFinished;
}

public synchronized void setIsFinished(boolean finished)
{
    isFinished = finished;
}

そして、これらのメソッドを使用して、isFinishedフラグを取得または設定します。ここでの同期の欠如を考えると、r.run()メソッドと他の「オブザーバー」が同期なしでデータを共有しているかどうかによっては、他のスレッドセーフの奇妙なことが見られる場合があります。

于 2013-01-11T21:16:25.767 に答える
1

Runnableaのコンストラクターにaを渡して、ThreadのメソッドThread オーバーライドすることはほとんどありません。run()

次の2つのコードは、基本的に同じです。

Runnable r = new Runnable( )
{
    public void run( )
    {
        // do stuff...
    }
};

new Thread( r ).start( );

オーバーライドして同じことを達成する別の方法がありますrun()

(new Thread( )
{
    public void run( )
    {
        // do stuff...
    }
}).start( );
于 2013-01-11T21:17:17.563 に答える
0

いいえ、runメソッドは単なる通常の関数であり、独自の動作を実装するためにThreadクラスを拡張するときにオーバーライドできます。

新しいスレッドを開始し、そのコードを非同期で実行するのは、Threadクラスのstartメソッドです。

于 2013-01-11T21:15:54.307 に答える
0

あなたのコードは部分的に正しく、部分的に間違っています。

isFinishedパラメータで渡したランナブル内のすべての実行が終了した場合にのみtrueに設定されるのは正しいです。

ただし、Javaメモリモデルの特定のセマンティクス(これについては以下で詳しく説明します)により、 trueに設定isFinishedすると、その変更は、その変数をtrueに設定したスレッドにのみ表示される可能性があります。 。isFinishedコードを期待どおりに機能させるには、揮発性として宣言する必要があります。これにより、その変数に加えた変更が他のスレッドからすぐに表示されます。

これを行う別の方法isFinishedは、ブール値ではなくAtomicBooleanとして宣言することです。このクラスには、ブール値をアトミックな方法でチェックおよび設定できる多くのメソッドがあり、多くの一般的なマルチスレッドの落とし穴を回避するのに役立ちます。

于 2013-01-11T21:16:16.880 に答える
0

コードの記述方法は、完了isFinishedするまでtrueに設定されません。r.run()同期の欠落または揮発性宣言の欠落が原因でデータの可視性の問題が発生する可能性があるため、別の方法で表示される可能性があります。

両方ともRunnableをコンストラクターに渡しているので少し奇妙ですが、スレッド内ではなく、メソッド宣言からの参照を使用して呼び出しています。しかし、それは「機能」し、そこには冗長性があります。

余談ですが@Override、匿名のクラスで忘れないでください:)

于 2013-01-11T21:16:44.503 に答える
0

問題のために特別に設計された同期プリミティブを使用することをお勧めします。

このプリミティブはCountDownLatchと呼ばれます。

更新されたコードは次のとおりです。

class Task {

  String taskName;
  private Thread thread;
  CountDownLatch finishedSignal = new CountDownLatch( 1 );

  public Task(String name){
    taskName = name;
  }

  public void createTask(final Runnable r) {
    thread = new Thread(r){
        public void run(){
            if(r != null) {
                r.run();
                finishedSignal.countDown( );
            }
        }
    };
    thread.start();

    finishedSignal.await( );
  }
}
于 2013-01-11T21:26:18.827 に答える
0

独自のTaskクラスの代わりにFutureTaskを使用する必要があります。isDone()メソッドがあり、Executorフレームワークとうまく統合されています。

最も重要なのは、発生する前の関係が期待どおりに維持されることです(実際のコードでは、Runnable内のすべてのコードが終了する前に、isFinishedがtrueに設定されているという問題ではありませんが、逆の場合は、 Runnableが終了している場合でも、元のスレッドではtrue)

例:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("work done");
    }
};

FutureTask<Void> task = new FutureTask<Void>(runnable, null);
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit (task);

while (!task.isDone()) {
    System.out.println("waiting...");
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
于 2013-01-11T21:30:07.330 に答える