概要
synchronized
による排他制御やatomic
パッケージのクラスでは実現できない、複数のメソッドにまたがる排他制御を実現できる。
特徴
- スレッドセーフであり、複数のスレッドから同時にアクセスされても、正しくロックを制御する。
- 排他的なロック(獲得できるのは1つのスレッドだけ)を提供する。ロックを獲得したスレッドだけが保持し、他のスレッドはロックの解放を待つ必要がある。
- 同じスレッドがロックを再帰的に獲得(スレッドがすでにロックを保持している場合、再度ロックを獲得)することができる。
主なメソッド
-
lock()
:ロックを獲得する。
他のスレッドが既にロックを保持している場合は、呼び出し元のスレッドはロックの解放を待機する。同じスレッドが再帰的にロックを獲得することも可能。 -
unlock()
:ロックを解放する。
ロックを保持していたスレッドが解放を行い、他のスレッドがロックを獲得できるようになる。
対応するlock()
の呼び出しとペアで使用する必要がある。 -
tryLock()
:ブロックせずにロックの獲得を試みる。- 他のスレッドが既にロックを保持している場合:
false
を返す。 - 獲得できた場合:
true
を返す。
- 他のスレッドが既にロックを保持している場合:
-
lockInterruptibly()
:割り込み可能な形式でロックを獲得する。
他のスレッドが既にロックを保持している場合は、lockInterruptibly()
の呼び出しはブロックされ、他のスレッドが割り込み要求を送信した場合にはInterruptedException
をスローする。 -
isHeldByCurrentThread()
:現在のスレッドがロックを保持しているかどうかを判定する。- ロックを保持している場合:
true
を返す。 - 保持していない場合:
false
を返す。
- ロックを保持している場合:
-
getHoldCount()
:現在のスレッドがロックを獲得した回数を取得する。同じスレッドが再帰的にロックを獲得した場合、その回数が返される。 -
isLocked()
:ロックが獲得されているかどうかを判定する。- ロックが獲得されている場合:
true
を返す。 - 獲得されていない場合:
false
を返す。
- ロックが獲得されている場合:
例
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread(() -> {
performTask("Thread 1");
}).start();
new Thread(() -> {
performTask("Thread 2");
}).start();
}
private static void performTask(String threadName) {
lock.lock();
try {
System.out.println("Starting task from " + threadName);
Thread.sleep(1000);
System.out.println("Task completed from " + threadName);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
この例では、ReentrantLock
を使用して2つのスレッドが同じロックを制御する。
各スレッドはperformTask
メソッドを呼び出し、ロックを獲得する。performTask
メソッド内で、スレッドはタスクを開始し、一定時間待機後、タスクを完了する。
実行すると、2つのスレッドが交互にタスクを実行するが、ロックメカニズムにより同時にタスクを実行することはない。
1つのスレッドがロックを保持している場合、もう一つのスレッドはロックの解放を待つ。
実行結果
Starting task from Thread 1
Task completed from Thread 1
Starting task from Thread 2
Task completed from Thread 2
上記の例では、ReentrantLock
を使用してスレッド間の排他制御を実現している。
ロックの獲得と解放が正しく行われ、スレッド間でタスクの順序が保持されることが確認できる。
ロックと解除までの処理はtry
ブロックで囲み、解除はfinally
ブロックで記述する。
これにより、いずれかのスレッドに何らかの例外が発生した場合でもロックは解除されるため、別のスレッドが処理を続けることができる。