4

レンダリングスレッドを持つ小さなアプリがあります。このスレッドが行うのは、オブジェクトを現在の場所に描画することだけです。

私は次のようなコードを持っています:

public void render()
{
    // ... rendering various objects

    if (mouseBall != null) mouseBall.draw()

}

次に、ユーザーがマウスをクリックしたときにmouseBallを作成して新しいボールに設定するマウスハンドラーもあります。次に、ユーザーはマウスをドラッグして、ボールがマウスの移動先をたどります。ユーザーがボールを放すと、mouseBall=nullを設定する別のマウスイベントが発生します。

問題は、レンダリングループが十分に高速に実行されているため、ランダムな時間に条件付き(mouseBall!= null)がtrueを返すことですが、その1秒後にユーザーがマウスを離すと、nullポインターが取得されます。 nullオブジェクトで.draw()を試行する場合の例外。

このような問題の解決策は何ですか?

4

3 に答える 3

13

問題は、2回アクセスしているという事実にあります。1mouseBall回はアクセスしていないかどうかを確認するためnull、もう1回は関数を呼び出すためです。この問題は、次のような一時的なものを使用することで回避できます。

public void render()
{
    // ... rendering various objects
    tmpBall = mouseBall;
    if (tmpBall != null) tmpBall.draw();
}
于 2008-12-13T21:17:13.153 に答える
9

ifステートメントとdrawステートメントを同期して、1つのアトミックシーケンスとして実行されることが保証されるようにする必要があります。Javaでは、これは次のように行われます。

    
public void render()
{
    // ... rendering various objects
    synchronized(this) {
        if (mouseBall != null) mouseBall .draw();
   }
}
于 2008-12-13T21:25:01.187 に答える
2

すでに他の回答を受け入れていることは知っていますが、3 つ目のオプションは、java.util.concurrent.atomic パッケージの AtomicReference クラスを使用することです。これにより、サポート コードを必要とせずにアトミックに動作する取得、更新、および比較操作が提供されます。あなたの例では:

public void render()
{
    AtomicReference<MouseBallClass> mouseBall = ...;

    // ... rendering various objects
    MouseBall tmpBall = mouseBall.get();
    if (tmpBall != null) tmpBall.draw();
}

これはグレッグのソリューションと非常によく似ており、概念的には、値の鮮度を確保するためにボラティリティを使用し、値を使用する前に条件を適用するために一時的なコピーを取得するという点で似ています。

したがって、ここで使用されている正確な例は、AtomicReferences の能力を示すにはあまり適していません。代わりに、他のスレッドが既に null の場合にのみマウスボール変数を更新することを検討してください。これは、さまざまな初期化スタイルのコード ブロックに役立つイディオムです。この場合、通常は同期を使用することが不可欠です。チェックしてボールがnullであることがわかった場合でも、ボールを設定しようとしたときにまだnullになるようにします(そうしないと、元の問題の領域に戻ります) )。ただし、AtomicReference を使用すると、次のように簡単に言うことができます。

mouseBall.compareAndSet(null, possibleNewBall);

これはアトミック操作であるため、1 つのスレッドが値を null として「認識」すると、他のスレッドがそれを読み取る機会を得る前に、それを possibleNewBall 参照に設定します。

アトミック参照のもう 1 つの優れたイディオムは、無条件に何かを設定しているが、古い値で何らかのクリーンアップを実行する必要がある場合です。その場合、次のように言えます。

   MouseBall oldBall = mouseBall.getAndSet(newMouseBall);
   // Cleanup code using oldBall

AtomicIntegers には、これらの利点とそれ以上の利点があります。getAndIncrement() メソッドは、スレッドのインターリーブに関係なく、各呼び出しが個別の値を返すことを保証できるため、グローバルに共有されたカウンターに最適です。手間を最小限に抑えたスレッドセーフ。

于 2009-01-09T18:10:13.943 に答える