Java : Lock (同期) - API使用例
Lock (Java SE 19 & JDK 19) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。
概要
Lock インタフェースを使うと、synchronized 文に相当する同期処理を、より柔軟に構築することができます。
主な実装には ReentrantLock クラスがあります。
例えば、synchronized 文は、同期の開始と終了はブロック単位でしか行うことができません。
class Sample {
private final Object obj = new Object();
void func() {
synchronized (obj) { // 同期開始
...
} // 同期終了
}
}
Lock を使うと、ブロック単位でなくても同期を開始、終了することができます。
class Sample {
private final Lock lock = new ReentrantLock();
void funcA() {
lock.lock(); // 同期開始
...
}
void funcB() {
lock.unlock(); // 同期終了
...
}
}
ただし、柔軟になった代わりにリスクは高くなります。
プログラマは、unlock 漏れがないように細心の注意を払う必要があります。
もし synchronized文で済むのであれば、わざわざ Lockインタフェースを使う必要はないでしょう。
try (final var executor = Executors.newFixedThreadPool(3)) {
final Lock 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!)
メソッド
void lock ()
try (final var executor = Executors.newFixedThreadPool(3)) {
final Lock 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!)
void lockInterruptibly ()
try (final var executor = Executors.newFixedThreadPool(3)) {
final Lock 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 Lock 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 Lock 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.)
boolean tryLock ()
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;
try (var executor = Executors.newFixedThreadPool(2)) {
final Lock 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 time, TimeUnit unit)
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;
try (var executor = Executors.newFixedThreadPool(2)) {
final Lock 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 ()
このメソッドの使用例は、lock() にまとめて記載しました。
そちらの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