ReentrantLock と synchronized の比較
ReentrantLock の機能(tryLock、lockInterruptibly、公平性)を synchronized キーワードと比較します。
「ReentrantLock と synchronized の比較」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
synchronizedキーワード
Javaのsynchronizedキーワードは、オブジェクトの固有ロック(モニター)を使って相互排他を実現します。シンプルですが、タイムアウト、公平性、割り込み可能性には対応していません。
class Counter {
private int count = 0;
synchronized void increment() {
count++; // only one thread at a time
}
synchronized int get() { return count; }
}
Counter c = new Counter();
// Multiple threads can safely call increment()ReentrantLockの基本
ReentrantLockは、java.util.concurrent.locksにある明示的なロックです。synchronizedと同じ相互排他を提供しますが、より細かく制御できます。ロックはfinallyブロックで手動で解放する必要があります。
import java.util.concurrent.locks.*;
class SafeCounter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // always release in finally!
}
}
int get() {
lock.lock();
try { return count; }
finally { lock.unlock(); }
}
}tryLock: ブロッキングなしの取得
tryLock()は、ブロックせずにロックの取得を試みます。取得に成功するとtrue、別のスレッドがロックを保持している場合はfalseを返します。
ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) {
try {
System.out.println("Got the lock, doing work");
} finally {
lock.unlock();
}
} else {
System.out.println("Lock busy, skipping or retrying");
}タイムアウト付きtryLock
指定した時間までロックの取得を待機します:
try {
if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
try {
// work with protected resource
System.out.println("Acquired within 500ms");
} finally {
lock.unlock();
}
} else {
System.out.println("Timed out waiting for lock");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}lockInterruptibly
lockInterruptibly()は、スレッドが割り込まれない限りロックを取得します。ロック待ち中のキャンセル可能なタスクに便利です:
try {
lock.lockInterruptibly(); // throws InterruptedException if interrupted
try {
// do work
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("Interrupted while waiting for lock");
Thread.currentThread().interrupt();
}再入性
synchronizedとReentrantLockはいずれも再入可能です。すでにロックを保持しているスレッドは、デッドロックを起こさずに同じロックを再び取得できます:
synchronized void outer() {
System.out.println("outer");
inner(); // same thread re-enters — OK
}
synchronized void inner() {
System.out.println("inner"); // same lock, same thread
}
// ReentrantLock:
ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.lock(); // acquire again — hold count = 2
lock.unlock(); // hold count = 1
lock.unlock(); // hold count = 0, lock released公平性ポリシー
公平なReentrantLockを作成すると、FIFO順にアクセスを許可できます。スループットは低下しますが、スレッドの飢餓を防げます:
// Unfair (default): no ordering guarantee, better throughput
ReentrantLock unfair = new ReentrantLock();
// Fair: threads acquire in arrival order
ReentrantLock fair = new ReentrantLock(true);
System.out.println(fair.isFair()); // trueCondition変数
ReentrantLockはConditionオブジェクトを提供します。wait()/notify()より柔軟に使用できます:
ReentrantLock lock2 = new ReentrantLock();
Condition notEmpty = lock2.newCondition();
Queue<String> queue = new LinkedList<>();
// Producer
lock2.lock();
try {
queue.offer("item");
notEmpty.signal(); // wake one waiting thread
} finally { lock2.unlock(); }
// Consumer
lock2.lock();
try {
while (queue.isEmpty()) notEmpty.await(); // wait & release lock
System.out.println(queue.poll());
} finally { lock2.unlock(); }ReentrantLockとsynchronizedの使い分け
synchronizedを使用する場合:
- シンプルな相互排他が必要な場合
- タイムアウト、公平性、割り込み可能性が不要な場合
ReentrantLockを使用する場合:
- tryLock()またはtryLock(timeout)が必要な場合
- lockInterruptibly()が必要な場合
- 公平な順序制御が必要な場合
- 複数のCondition変数が必要な場合
ロック数と監視
ReentrantLockは診断用メソッドを提供します:
ReentrantLock lock3 = new ReentrantLock();
lock3.lock();
lock3.lock(); // reentrant — hold count 2
System.out.println(lock3.getHoldCount()); // 2
System.out.println(lock3.isHeldByCurrentThread()); // true
System.out.println(lock3.isLocked()); // true
System.out.println(lock3.getQueueLength()); // 0 (no waiting threads)
lock3.unlock();
lock3.unlock(); // fully releasedよくあるミス: ロックせずにunlockする
現在のスレッドがロックを保持していない状態でunlock()を呼び出すと、IllegalMonitorStateExceptionがスローされます。必ずlockとunlockを組み合わせるか、try-finallyを使用してください:
ReentrantLock lock4 = new ReentrantLock();
try {
// If lock() is never called (e.g., skipped by exception before this line)
lock4.unlock(); // throws IllegalMonitorStateException
} catch (IllegalMonitorStateException e) {
System.out.println("Must lock before unlock!");
}クイックチェック
ロックが使用できない場合にブロックせず、ロックを取得するかすぐに戻ることができるReentrantLockのメソッドはどれですか?
まとめ: ReentrantLockとsynchronized
重要なポイント:
- どちらも相互排他と再入性を提供します
- ReentrantLockはtryLock、lockInterruptibly、公平性、Conditionを追加で提供します
- 必ずfinallyブロックで解放してください
- synchronizedのほうがシンプルです。その機能で十分な場合はsynchronizedを優先してください
- タイムアウト、割り込み可能性、複数のConditionが必要な高度な状況ではReentrantLockを使用します
よくある質問
「ReentrantLock と synchronized の比較」レッスンは無料ですか?
はい。「ReentrantLock と synchronized の比較」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「ReentrantLock と synchronized の比較」で何を学びますか?
ReentrantLock の機能(tryLock、lockInterruptibly、公平性)を synchronized キーワードと比較します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「ReentrantLock と synchronized の比較」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ReentrantLock と synchronized の比較
- 読み取り・書き込み処理のための ReadWriteLock
- Atomic 変数:ロックなしの更新
- StampedLock と楽観的読み取り