Java : ReentrantLock (同期) - API使用例
ReentrantLock (Java SE 20 & JDK 20) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。
概要
ReentrantLock クラスは、Lock インタフェースの代表的な実装です。
synchronized 文に相当する同期処理を、より柔軟に構築することができます。
例えば、synchronized 文は、同期の開始と終了はブロック単位でしか行うことができません。
class Sample {
private final Object obj = new Object();
void func() {
synchronized (obj) { // 同期開始
...
} // 同期終了
}
}
Lock を使うと、ブロック単位でなくても同期を開始、終了することができます。
class Sample {
private final var lock = new ReentrantLock();
void funcA() {
lock.lock(); // 同期開始
...
}
void funcB() {
lock.unlock(); // 同期終了
...
}
}
ただし、柔軟になった代わりにリスクは高くなります。
プログラマは、unlock 漏れがないように細心の注意を払う必要があります。
もし synchronized文で済むのであれば、わざわざ Lockインタフェースを使う必要はないでしょう。
try (final var executor = Executors.newFixedThreadPool(3)) {
final var lock = new ReentrantLock();
for (int i = 1; i <= 3; i++) {
final var id = i;
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
System.out.println(id + " : lock ...");
lock.lock();
try {
System.out.println(id + " : lock OK!");
TimeUnit.SECONDS.sleep(2);
System.out.println(id + " : sleep completed (unlock!)");
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
}
}
// 結果
// ↓
//1 : lock ...
//1 : lock OK!
//2 : lock ...
//3 : lock ...
//1 : sleep completed (unlock!)
//2 : lock OK!
//2 : sleep completed (unlock!)
//3 : lock OK!
//3 : sleep completed (unlock!)
コンストラクタ
ReentrantLock ()
try (final var executor = Executors.newFixedThreadPool(3)) {
final var lock = new ReentrantLock();
for (int i = 1; i <= 3; i++) {
final var id = i;
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
System.out.println(id + " : lock ...");
lock.lock();
try {
System.out.println(id + " : lock OK!");
TimeUnit.SECONDS.sleep(2);
System.out.println(id + " : sleep completed (unlock!)");
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
}
}
// 結果
// ↓
//1 : lock ...
//1 : lock OK!
//2 : lock ...
//3 : lock ...
//1 : sleep completed (unlock!)
//2 : lock OK!
//2 : sleep completed (unlock!)
//3 : lock OK!
//3 : sleep completed (unlock!)
ReentrantLock (boolean fair)
fair パラメータ以外については ReentrantLock() の使用例もご参照ください。
void testFair(boolean fair) throws InterruptedException {
final var lock = new ReentrantLock(fair);
System.out.println("isFair = " + lock.isFair());
final var order = new ArrayList<Integer>();
try (final var executor = Executors.newFixedThreadPool(2)) {
lock.lock();
try {
executor.submit(() -> {
try {
lock.lock();
order.add(1);
} finally {
lock.unlock();
}
try {
lock.lock();
order.add(2);
} finally {
lock.unlock();
}
});
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
try {
lock.lock();
order.add(3);
} finally {
lock.unlock();
}
});
TimeUnit.MILLISECONDS.sleep(100);
} finally {
lock.unlock();
}
}
System.out.println("order = " + order);
}
testFair(true);
// 結果
// ↓
//isFair = true
//order = [1, 3, 2]
testFair(false);
// 結果
// ↓
//isFair = false
//order = [1, 2, 3]
メソッド
int getHoldCount ()
final var lock = new ReentrantLock();
System.out.println(lock.getHoldCount()); // 0
System.out.println(lock.isHeldByCurrentThread()); // false
lock.lock();
try {
System.out.println(lock.getHoldCount()); // 1
System.out.println(lock.isHeldByCurrentThread()); // true
lock.lock();
try {
System.out.println(lock.getHoldCount()); // 2
System.out.println(lock.isHeldByCurrentThread()); // true
lock.lock();
try {
System.out.println(lock.getHoldCount()); // 3
System.out.println(lock.isHeldByCurrentThread()); // true
} finally {
lock.unlock();
}
System.out.println(lock.getHoldCount()); // 2
System.out.println(lock.isHeldByCurrentThread()); // true
} finally {
lock.unlock();
}
System.out.println(lock.getHoldCount()); // 1
System.out.println(lock.isHeldByCurrentThread()); // true
} finally {
lock.unlock();
}
System.out.println(lock.getHoldCount()); // 0
System.out.println(lock.isHeldByCurrentThread()); // false
protected Thread getOwner ()
protected です。
独自にサブクラスを作ることは少ないと思いますので、コード例は割愛します。
protected Collection<Thread> getQueuedThreads ()
protected です。
独自にサブクラスを作ることは少ないと思いますので、コード例は割愛します。
final int getQueueLength ()
final var lock = new ReentrantLock();
try (final var executor = Executors.newFixedThreadPool(5)) {
for (int i = 1; i <= 5; i++) {
final var id = i;
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
System.out.printf("%d : lock ... : queue length = %d (%b)%n",
id, lock.getQueueLength(), lock.hasQueuedThreads());
lock.lock();
try {
System.out.printf("%d : lock OK! : queue length = %d (%b)%n",
id, lock.getQueueLength(), lock.hasQueuedThreads());
TimeUnit.SECONDS.sleep(3);
System.out.println(id + " : sleep completed (unlock!)");
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
}
}
// 結果
// ↓
//1 : lock ... : queue length = 0 (false)
//1 : lock OK! : queue length = 0 (false)
//2 : lock ... : queue length = 0 (false)
//3 : lock ... : queue length = 1 (true)
//4 : lock ... : queue length = 2 (true)
//5 : lock ... : queue length = 3 (true)
//1 : sleep completed (unlock!)
//2 : lock OK! : queue length = 3 (true)
//2 : sleep completed (unlock!)
//3 : lock OK! : queue length = 2 (true)
//3 : sleep completed (unlock!)
//4 : lock OK! : queue length = 1 (true)
//4 : sleep completed (unlock!)
//5 : lock OK! : queue length = 0 (false)
//5 : sleep completed (unlock!)
protected Collection<Thread> getWaitingThreads (Condition condition)
protected です。
独自にサブクラスを作ることは少ないと思いますので、コード例は割愛します。
int getWaitQueueLength (Condition condition)
try (var executor = Executors.newFixedThreadPool(3)) {
final var lock = new ReentrantLock();
final var cond = lock.newCondition();
for (int i = 1; i <= 3; i++) {
final var id = i;
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
lock.lock();
try {
System.out.printf("%d : await ... : wait queue length = %d (%b)%n",
id, lock.getWaitQueueLength(cond), lock.hasWaiters(cond));
cond.await();
System.out.printf("%d : await OK! : wait queue length = %d (%b)%n",
id, lock.getWaitQueueLength(cond), lock.hasWaiters(cond));
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
}
TimeUnit.SECONDS.sleep(2);
for (int i = 0; i < 3; i++) {
lock.lock();
try {
System.out.println("signal!");
cond.signal();
} finally {
lock.unlock();
}
TimeUnit.SECONDS.sleep(1);
}
}
// 結果
// ↓
//1 : await ... : wait queue length = 0 (false)
//2 : await ... : wait queue length = 1 (true)
//3 : await ... : wait queue length = 2 (true)
//signal!
//1 : await OK! : wait queue length = 2 (true)
//signal!
//2 : await OK! : wait queue length = 1 (true)
//signal!
//3 : await OK! : wait queue length = 0 (false)
final boolean hasQueuedThread (Thread thread)
try (var executor = Executors.newSingleThreadExecutor()) {
final var lock = new ReentrantLock();
final var main = Thread.currentThread();
executor.submit(() -> {
try {
lock.lock();
try {
System.out.println("A : lock OK!");
TimeUnit.SECONDS.sleep(1);
System.out.println("A : main is waiting = " + lock.hasQueuedThread(main));
TimeUnit.SECONDS.sleep(2);
System.out.println("A : main is waiting = " + lock.hasQueuedThread(main));
System.out.println("A : unlock!");
} finally {
lock.unlock();
}
TimeUnit.SECONDS.sleep(2);
System.out.println("A : main is waiting = " + lock.hasQueuedThread(main));
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
}
});
TimeUnit.SECONDS.sleep(2);
System.out.println("main : lock ...");
lock.lock();
try {
System.out.println("main : lock OK!");
TimeUnit.SECONDS.sleep(1);
System.out.println("main : unlock!");
} finally {
lock.unlock();
}
}
// 結果
// ↓
//A : lock OK!
//A : main is waiting = false
//main : lock ...
//A : main is waiting = true
//A : unlock!
//main : lock OK!
//main : unlock!
//A : main is waiting = false
final boolean hasQueuedThreads ()
このメソッドの使用例は、getQueueLength() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
boolean hasWaiters (Condition condition)
このメソッドの使用例は、getWaitQueueLength(Condition condition) にまとめて記載しました。
そちらのAPI使用例をご参照ください。
final boolean isFair ()
このメソッドの使用例は、ReentrantLock(boolean fair) にまとめて記載しました。
そちらのAPI使用例をご参照ください。
boolean isHeldByCurrentThread ()
このメソッドの使用例は、getHoldCount() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
boolean isLocked ()
final var lock = new ReentrantLock();
try (final var executor = Executors.newFixedThreadPool(3)) {
for (int i = 1; i <= 3; i++) {
final var id = i;
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
System.out.printf("%d : lock ... : isLocked = %b%n", id, lock.isLocked());
lock.lock();
try {
System.out.printf("%d : lock OK! : isLocked = %b%n", id, lock.isLocked());
TimeUnit.SECONDS.sleep(2);
System.out.printf("%d : sleep completed (unlock!)%n", id);
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
}
}
System.out.printf("main : isLocked = %b%n", lock.isLocked());
// 結果
// ↓
//1 : lock ... : isLocked = false
//1 : lock OK! : isLocked = true
//2 : lock ... : isLocked = true
//3 : lock ... : isLocked = true
//1 : sleep completed (unlock!)
//2 : lock OK! : isLocked = true
//2 : sleep completed (unlock!)
//3 : lock OK! : isLocked = true
//3 : sleep completed (unlock!)
//main : isLocked = false
void lock ()
このメソッドの使用例は、ReentrantLock() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
void lockInterruptibly ()
try (final var executor = Executors.newFixedThreadPool(3)) {
final var lock = new ReentrantLock();
for (int i = 1; i <= 3; i++) {
final var id = i;
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
try {
System.out.println(id + " : lock ...");
lock.lockInterruptibly();
try {
System.out.println(id + " : lock OK!");
TimeUnit.SECONDS.sleep(2);
System.out.println(id + " : sleep completed (unlock!)");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
}
});
}
}
// 結果
// ↓
//1 : lock ...
//1 : lock OK!
//2 : lock ...
//3 : lock ...
//1 : sleep completed (unlock!)
//2 : lock OK!
//2 : sleep completed (unlock!)
//3 : lock OK!
//3 : sleep completed (unlock!)
try (final var executor = Executors.newFixedThreadPool(2)) {
final var lock = new ReentrantLock();
class Task implements Runnable {
private final String name;
Task(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(name + " : lock ...");
lock.lockInterruptibly();
try {
System.out.println(name + " : lock OK!");
TimeUnit.SECONDS.sleep(2);
System.out.println(name + " : sleep completed (unlock!)");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println(name + " : InterruptedException!");
}
}
}
executor.submit(new Task("A"));
TimeUnit.MILLISECONDS.sleep(100);
final var futureB = executor.submit(new Task("B"));
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("cancel task(B)");
futureB.cancel(true);
}
// 結果
// ↓
//A : lock ...
//A : lock OK!
//B : lock ...
//cancel task(B)
//B : InterruptedException!
//A : sleep completed (unlock!)
Condition newCondition ()
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;
try (var executor = Executors.newSingleThreadExecutor()) {
final var lock = new ReentrantLock();
final var cond = lock.newCondition();
executor.submit(() -> {
System.out.printf("lock ... (%f sec.)%n", elapsedTime.getAsDouble());
lock.lock();
try {
System.out.printf("await ... (%f sec.)%n", elapsedTime.getAsDouble());
cond.await();
System.out.printf("await OK! (%f sec.)%n", elapsedTime.getAsDouble());
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
TimeUnit.SECONDS.sleep(5);
lock.lock();
try {
System.out.printf("signal! (%f sec.)%n", elapsedTime.getAsDouble());
cond.signal();
} finally {
lock.unlock();
}
}
// 結果
// ↓
//lock ... (0.003795 sec.)
//await ... (0.006260 sec.)
//signal! (5.019196 sec.)
//await OK! (5.019534 sec.)
String toString ()
final var lock = new ReentrantLock();
final var str1 = lock.toString();
// java.util.concurrent.locks.ReentrantLock@20bd8be5[Unlocked]
System.out.println(str1);
lock.lock();
try {
final var str2 = lock.toString();
// java.util.concurrent.locks.ReentrantLock@20bd8be5[Locked by thread Test worker]
System.out.println(str2);
} finally {
lock.unlock();
}
boolean tryLock ()
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;
try (var executor = Executors.newFixedThreadPool(2)) {
final var lock = new ReentrantLock();
executor.submit(() -> {
lock.lock();
try {
System.out.println("A : lock OK!");
TimeUnit.SECONDS.sleep(5);
System.out.println("A : sleep completed (unlock!)");
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
try {
while (true) {
if (lock.tryLock()) {
try {
System.out.printf("B : tryLock = true (%f sec.)%n",
elapsedTime.getAsDouble());
break;
} finally {
lock.unlock();
}
}
System.out.printf("B : tryLock = false (%f sec.)%n",
elapsedTime.getAsDouble());
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
}
});
}
// 結果
// ↓
//A : lock OK!
//B : tryLock = false (0.113863 sec.)
//B : tryLock = false (2.125517 sec.)
//B : tryLock = false (4.135200 sec.)
//A : sleep completed (unlock!)
//B : tryLock = true (6.139156 sec.)
boolean tryLock (long timeout, TimeUnit unit)
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;
try (var executor = Executors.newFixedThreadPool(2)) {
final var lock = new ReentrantLock();
executor.submit(() -> {
lock.lock();
try {
System.out.println("A : lock OK!");
TimeUnit.SECONDS.sleep(5);
System.out.println("A : sleep completed (unlock!)");
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
} finally {
lock.unlock();
}
});
TimeUnit.MILLISECONDS.sleep(100);
executor.submit(() -> {
try {
while (true) {
if (lock.tryLock(2, TimeUnit.SECONDS)) {
try {
System.out.printf("B : tryLock = true (%f sec.)%n",
elapsedTime.getAsDouble());
break;
} finally {
lock.unlock();
}
}
System.out.printf("B : tryLock = false (%f sec.)%n",
elapsedTime.getAsDouble());
}
} catch (InterruptedException e) {
System.out.println("InterruptedException!");
}
});
}
// 結果
// ↓
//A : lock OK!
//B : tryLock = false (2.114785 sec.)
//B : tryLock = false (4.131868 sec.)
//A : sleep completed (unlock!)
//B : tryLock = true (5.011976 sec.)
void unlock ()
このメソッドの使用例は、ReentrantLock() にまとめて記載しました。
そちらのAPI使用例をご参照ください。
関連記事
- API 使用例
- BlockingQueue (ブロッキング・キュー)
- Callable
- CancellationException
- ConcurrentHashMap.KeySetView (並列処理用セット)
- ConcurrentLinkedDeque (並列処理用・両端キュー)
- ConcurrentLinkedQueue (並列処理用キュー)
- ConcurrentMap (並列処理用マップ)
- ConcurrentModificationException (並列処理例外)
- ConcurrentSkipListSet (並列処理用セット)
- Condition (同期)
- CopyOnWriteArrayList (並列処理用リスト)
- CopyOnWriteArraySet (並列処理用セット)
- CountDownLatch (同期)
- CyclicBarrier (同期)
- Exchanger (同期)
- Executor
- ExecutorService
- Executors
- Future
- Future.State
- FutureTask
- InterruptedException (割込み例外)
- Lock (同期)
- Object (オブジェクト)
- Runnable
- Semaphore (セマフォ)
- Thread (スレッド)
- ThreadGroup
- ThreadLocal
- TimeUnit