すでに他の回答を受け入れていることは知っていますが、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() メソッドは、スレッドのインターリーブに関係なく、各呼び出しが個別の値を返すことを保証できるため、グローバルに共有されたカウンターに最適です。手間を最小限に抑えたスレッドセーフ。