Programmingバックエンド開発者

Javaにおけるsynchronizedブロックの動作について説明してください。その特徴、同期のためのオブジェクトの選択方法、および不適切な選択がどのようにエラーにつながるかについて説明してください。

Hintsage AIアシスタントで面接を突破

答え。

synchronizedは、クリティカルセクションへのスレッドセーフなアクセスを実現するためのキーワードです。それはメソッド(例えば、public synchronized void foo())やコードブロック(synchronized(obj) { ... })のいずれかに使用できます。スレッドがsynchronizedブロックに入ると、そのオブジェクトのモニター(ロック)を取得します。モニターが使用されている間、他のスレッドは同じオブジェクトをモニターとして使用する別のsynchronizedブロックに入ることができません。

特徴:

  • 同じオブジェクトによる同期は相互排他を確保します(同時に1つのスレッドのみがブロックを実行できます)。
  • thisや静的オブジェクト(例えば、クラス)が同期対象として使用でき、任意のオブジェクトでも可能です。
  • スレッドが多数存在するオブジェクトや、あまりにもローカルなオブジェクトをモニターとして選択する場合、スレッドセーフ性が損なわれる可能性があります。

同期オブジェクトの選択例

public class Counter { private int count; private final Object lock = new Object(); // プライベートオブジェクト public void increment() { synchronized(lock) { count++; } } }

なぜプライベートなロックを使用する方が良いのか? 公開されたオブジェクト(例えば、thisや共有可能な文字列)に対して同期を行うと、外部コードがこのモニターを奪う可能性があり、それがデッドロックや予期しない動作を引き起こすことになります。

トリッキーな質問。

質問: 固定値を含むString型のオブジェクトで同期するとどうなりますか?

答え: Javaの文字列はインターンされているため(同じリテラルに対して同じオブジェクトが使用されます)、例えばsynchronized("lock")で同期した場合、同じリテラルで同期している他のコードと衝突する可能性があり、プログラムの全く異なる部分間で予期しないブロックが発生する可能性があります。

例(こうするべきではない):

synchronized("LOCK") { ... }

このテーマの微妙さから生じた実際のエラーの例。


物語

多スレッドの取引システムで公開されたオブジェクトを使用して同期を行った結果、外部コードがロックを取得できてしまい、異なるモジュール間でのスレッド間デッドロックが発生し、取引所での停止が生じました。


物語

若手開発者が文字列リテラルを用いてコレクションへのアクセスを同期した。他のコードも同じ値の文字列で同期しており、これによりこれらのスレッドが互いに待機することになり、ビジネスロジックが著しく遅くなりました。


物語

毎回新しいオブジェクトを選択して同期した結果:synchronized(new Object()) { ... }。その結果、同期は全く機能せず、異なるスレッドがデータに同時アクセスできる状況が生じました。これは負荷テストでのみ発見されました。