ランタイム環境やスレッドで使用される呼び出しメソッドに関係なく、複数のスレッドがクラスにアクセスする場合、実行が交互に実行され、メイン呼び出しコードで追加の同期や調整を行わなくても、このクラスが正しい動作を示すことができる場合、このクラスはスレッドセーフであると言われます。
ステートレス オブジェクトは、Servlet
のようにスレッドセーフである必要があります。
競合状態は、不適切な実行タイミングにより誤った結果が発生した場合に発生します。
「まず確認してから実行」操作は、可能かつ効果的な観察結果に基づいて次のアクションを決定することです。例: 遅延初期化。
if(instance == null) { instance = new SomeObject(); }
「読み取り-変更-書き込み」操作の結果の状態は、前の状態によって異なります。例: インクリメント操作。
long count = 0; count++;
アトミック操作とは、同じ状態にアクセスするすべての操作 (操作自体を含む) について、この操作がアトミックな (分割不可能な) 操作で実行されることを意味します。
スレッドの安全性を確保するために、複合操作と呼ばれる、アトミックに実行する必要がある一連の操作が含まれています。
インクリメンタル操作では、既存のスレッド セーフ クラスを使用して、スレッド セーフを確保できます。例:
AtomicLong count = new AtomicLong(0); count.incrementAndGet();
クラスに状態変数が 1 つだけある場合は、スレッド セーフな状態変数を使用してクラスのスレッド セーフを確保できます。クラスにさらに多くの状態がある場合、スレッドセーフな状態変数を追加するだけでは十分ではありません。状態の一貫性を確保するには、関連するすべての状態変数を 1 回のアトミック操作で更新する必要があります。
Java は組み込みロックを提供します: 同期コード ブロック、これには以下が含まれます: ロックとしてのオブジェクト参照、このロックによって保護されるコードのブロックとしてのオブジェクト。
キーワード synchronized
で変更されたメソッドは、メソッド本体全体にわたる同期コード ブロックであり、同期コード ブロックのロックはメソッドが呼び出されるオブジェクトです。静的 synchronized
メソッドは、Class オブジェクトをロックとして使用します。
スレッドが同期されたコード ブロックに入ると、自動的にロックを取得し、スレッドが同期されたコード ブロックから出ると、自動的にロックを解放します。このロックを保持できるスレッドは最大 1 つであるため、同期されたコードはアトミックに実行されます。
組み込みロックは再入可能です。つまり、ロックを取得する操作の粒度は呼び出しではなくスレッドです。スレッドがすでに保持しているロックを再取得しようとすると、リクエストも成功します。
再入可能により、ロック動作のカプセル化がさらに改善され、オブジェクト指向の並行コードの開発が簡素化されます。
public class Widget { public synchronized void doSomething() { //...... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { //...... super.doSomething();//假如没有可重入的锁,该语句将产生死锁。 } }
複数のスレッドによって同時にアクセスされる可能性がある変更可能な状態変数の場合、アクセスするときに同じロックを保持する必要があります。 、状態変数はこのロックによって保護されていると言われます。
ロックを粗く使用するとスレッドの安全性が確保されますが、次のようなパフォーマンスの問題やアクティビティの問題が発生する可能性があります。サーブレットの同時実行性を高め、同期コード ブロックを縮小することでスレッドの安全性を維持します。アトミックな操作であるべきものを複数の同期コード ブロックに分割せず、共有状態に影響を与えず、実行に時間がかかる操作を同期コードから分離するようにしてください。例:
@ThreadSafe public class SynchronizedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastNumber; @GuardedBy("this") private BigInteger[] lastFactors; public synchronized void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); if (i.equals(lastNumber)) encodeIntoResponse(resp, lastFactors); else { BigInteger[] factors = factor(i);//因数分解计算 lastNumber = i; lastFactors = factors;//存放上一次计算结果 encodeIntoResponse(resp, factors); } } }
以上がJava 並行プログラミングでスレッド セーフを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。