Javaのスレッド処理は、Thread、Runnable、およびjava.util.concurrentパッケージのクラスを使用して実装されています。スレッドセーフティを確保するためには、さまざまな同期メカニズムが使用されます:
synchronized)を使用すると、複数のスレッドが競合することなく共有データにアクセスできます。ReentrantLock、Semaphore、AtomicInteger、ConcurrentHashMap)は、より柔軟な同期方法を提供します。同期の例:
public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }
原子クラスを使用した例:
import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }
volatileキーワードは、int型のカウンターのインクリメント時にスレッドセーフティを保証しますか?
回答: いいえ。volatileは、スレッド間の値の可視性のみを保証し、操作の原子性は保証しません。インクリメントは原子操作ではなく(count++は読み込み、増加、書き込みから成ります)、データの損失が発生する可能性があります。スレッドセーフなインクリメントには、同期やAtomicIntegerのようなクラスが必要です。
volatile int count = 0; // count++はスレッドセーフではありません!
物語
オンラインストアで、ボーナスカウントがvolatileカウンターを介して更新されていました。負荷が高まる中、数千人のユーザーが商品を注文し、一部のボーナスがcount++のスレッド競合により失われました。
物語
従業員は、マルチスレッドアプリケーション内のプロデューサーとコンシューマー間の共有バッファとして通常のArrayListを使用しており、その結果ConcurrentModificationExceptionが発生しました。解決策は、synchronizedブロックを使用するか、CopyOnWriteArrayListに置き換えることでした。
物語
支払い処理を行う開発者は、「金額を確認して引き落とす」という操作を2つの無関係なメソッドで行っていました。高負荷の状態では、原子トランザクションとロックを導入するまで、二重請求が発生していました。