Java Gold SE11対策:排他制御と synchronized の基本と深掘り

排他制御って何?
マルチスレッドで処理を並行実行すると、同じ変数やリソースに複数スレッドが同時にアクセスしてしまうことがあります。
その結果――
「誰かが書いてる途中の値を、別のスレッドが読み取ってしまう」
「同時に書き込みが走って、値が壊れる」
…といった「データの競合(レースコンディション)」が発生します。
この問題を防ぐには、同時に1つのスレッドだけが特定の処理に入れるようにする=排他制御(Mutual Exclusion)が必要です。
目次
Javaの排他制御:synchronized の使い方
方法①:同期メソッド
public synchronized void increment() {
count++;
}
- このメソッドは、このオブジェクト(this)に対してロックをかける
- 同時に呼ばれても、1スレッドずつしか実行されない
方法②:明示的にロック対象を指定(同期ブロック)
もっと細かく制御したいときは、synchronized(ロック対象) を使って、メソッドの一部だけロックできます。
public class Counter {
private int count = 0;
private final Object lock = new Object(); // 🔑ロック専用オブジェクト
public void increment() {
doSomething(); // 🔓ロック不要な処理
synchronized (lock) { // 🔒ここだけ排他制御
count++;
}
doSomethingElse(); // 🔓ロック不要な処理
}
public int getCount() {
return count;
}
}
lockって何?なぜ new Object()?
synchronized(lock) の lock には、Object型のインスタンスが必要です。
理由は「Objectにはロック(モニター)機能がある」から!
- Javaの synchronized(obj) は「objのモニター(内部ロック)」を使う
- だから Object 型である必要がある(String や Integer も technically OK)
しかし、new Object() が一番安全です。
- String や Integer は使い回されやすく、ロックの衝突リスクあり
- new Object() は完全に新規の、誰とも共有されないインスタンス
private final の意味は?
private final Object lock = new Object();
修飾子 | 意味 | 必須? |
private | 他のクラスから見えなくする | 推奨 |
final | 途中で他の値を代入できないようにする | 強く推奨 |
これによって、
- ロックが意図せず外部から変更されない
- 予期せぬロックの解除・再代入が防げる
つまり、「ロック専用オブジェクトとして最も安全な書き方」が:
private final Object lock = new Object();
synchronized はメソッドじゃない!構文です
synchronized (lock) {
// 処理
}
これはメソッドではなく、Javaのキーワード+構文。
- synchronized(…) { … } は「そのオブジェクトのロックを取得してから実行する」という構文
- コンパイル時に特別なバイトコードに変換される
if文のifとかと同じたぐい!
まとめ
項目 | ポイント |
synchronized | 排他制御のためのキーワード。メソッド修飾子にも、構文にも使える |
synchronized (lock) | lock オブジェクトのモニターを使って処理を排他制御 |
ロックに使うオブジェクト | 基本は new Object()。使い回しされにくく、安全 |
private final | ロック専用にし、他の場所から書き換えられないようにする |