広告

Java : Condition (同期) - API使用例

Condition (Java SE 19 & JDK 19) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。


概要

Conditionは、Object監視メソッド(wait、notify、およびnotifyAll)を別個のオブジェクトに分解し、それらに任意のLock実装の使用を組み合わせて、オブジェクトごとに複数の待機セットを保持する効果を付与します。 ここで、Lockはsynchronizedメソッドおよび文の使用を置き換え、ConditionはObject監視メソッドの使用を置き換えます。

クラス構成

Condition インタフェースは、Lock による同期処理において Object.wait, notify に相当する機能を実現します。

// 基準となる時刻
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.)

メソッド

void await ()

信号が送信されるか、割込みが発生するまで、現在のスレッドを待機させます。

// 基準となる時刻
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.)

boolean await (long time, TimeUnit unit)

信号が送信される、割込みが発生する、または指定された待機時間が経過するまで、現在のスレッドを待機させます。

// 基準となる時刻
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());

            final var ret = cond.await(2, TimeUnit.SECONDS);
            System.out.printf("await ret = %b (%f sec.)%n", ret, elapsedTime.getAsDouble());
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(1);

    lock.lock();
    try {
        System.out.printf("signal! (%f sec.)%n", elapsedTime.getAsDouble());
        cond.signal();
    } finally {
        lock.unlock();
    }
}

// 結果
// ↓
//lock ... (0.003034 sec.)
//await ... (0.004681 sec.)
//signal! (1.007004 sec.)
//await ret = true (1.007422 sec.)

タイムアウトする例です。

// 基準となる時刻
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());

            final var ret = cond.await(2, TimeUnit.SECONDS);
            System.out.printf("await ret = %b (%f sec.)%n", ret, elapsedTime.getAsDouble());
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(3);
}

// 結果
// ↓
//lock ... (0.001920 sec.)
//await ... (0.002397 sec.)
//await ret = false (2.015032 sec.)

long awaitNanos (long nanosTimeout)

信号が送信される、割込みが発生する、または指定された待機時間が経過するまで、現在のスレッドを待機させます。

// 基準となる時刻
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());

            final var ret = cond.awaitNanos(TimeUnit.SECONDS.toNanos(2));
            System.out.printf("await ret = %d (%f sec.)%n", ret, elapsedTime.getAsDouble());
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(1);

    lock.lock();
    try {
        System.out.printf("signal! (%f sec.)%n", elapsedTime.getAsDouble());
        cond.signal();
    } finally {
        lock.unlock();
    }
}

// 結果
// ↓
//lock ... (0.002420 sec.)
//await ... (0.003724 sec.)
//signal! (1.003412 sec.)
//await ret = 1000114200 (1.003748 sec.)

タイムアウトする例です。

// 基準となる時刻
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());

            final var ret = cond.awaitNanos(TimeUnit.SECONDS.toNanos(2));
            System.out.printf("await ret = %d (%f sec.)%n", ret, elapsedTime.getAsDouble());
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(3);
}

// 結果
// ↓
//lock ... (0.000975 sec.)
//await ... (0.001143 sec.)
//await ret = -15962300 (2.017199 sec.)

void awaitUninterruptibly ()

現在のスレッドを、信号が送られるまで待機させます。

InterruptedException が発生しない、ということ以外については await() の使用例をご参照ください。

try (var executor = Executors.newSingleThreadExecutor()) {
    final var lock = new ReentrantLock();
    final var cond = lock.newCondition();

    final var future = executor.submit(() -> {
        System.out.println("lock ...");
        lock.lock();
        try {
            System.out.println("await ...");
            cond.awaitUninterruptibly();
            System.out.println("await OK!");
            System.out.println("isInterrupted : " + Thread.currentThread().isInterrupted());
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(1);

    final var ret = future.cancel(true);
    System.out.println("cancel = " + ret);

    lock.lock();
    try {
        System.out.println("signal!");
        cond.signal();
    } finally {
        lock.unlock();
    }
}

// 結果
// ↓
//lock ...
//await ...
//cancel = true
//signal!
//await OK!
//isInterrupted : true
try (var executor = Executors.newSingleThreadExecutor()) {
    final var lock = new ReentrantLock();
    final var cond = lock.newCondition();

    final var future = executor.submit(() -> {
        System.out.println("lock ...");
        lock.lock();
        try {
            System.out.println("await ...");
            cond.await();
            System.out.println("await OK!");
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(1);

    final var ret = future.cancel(true);
    System.out.println("cancel = " + ret);
}

// 結果
// ↓
//lock ...
//await ...
//InterruptedException!
//cancel = true

boolean awaitUntil (Date deadline)

信号が送信される、割込みが発生する、または指定された期限が経過するまで、現在のスレッドを待機させます。

// 基準となる時刻
final long current = System.nanoTime();

// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;

final var deadline = Instant.now().plusSeconds(2);

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());

            final var ret = cond.awaitUntil(Date.from(deadline));
            System.out.printf("await ret = %b (%f sec.)%n", ret, elapsedTime.getAsDouble());
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(1);

    lock.lock();
    try {
        System.out.printf("signal! (%f sec.)%n", elapsedTime.getAsDouble());
        cond.signal();
    } finally {
        lock.unlock();
    }
}

// 結果
// ↓
//lock ... (0.003268 sec.)
//await ... (0.004692 sec.)
//signal! (1.007036 sec.)
//await ret = true (1.007370 sec.)

タイムアウトする例です。

// 基準となる時刻
final long current = System.nanoTime();

// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;

final var deadline = Instant.now().plusSeconds(2);

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());

            final var ret = cond.awaitUntil(Date.from(deadline));
            System.out.printf("await ret = %b (%f sec.)%n", ret, elapsedTime.getAsDouble());
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
        } finally {
            lock.unlock();
        }
    });

    TimeUnit.SECONDS.sleep(3);
}

// 結果
// ↓
//lock ... (0.000794 sec.)
//await ... (0.000954 sec.)
//await ret = false (2.012004 sec.)

void signal ()

待機中のスレッドを1つ起動します。

このメソッドの使用例は、await() にまとめて記載しました。
そちらのAPI使用例をご参照ください。

void signalAll ()

待機中のすべてのスレッドを起動します。

// 基準となる時刻
final long current = System.nanoTime();

// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;

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 ... (%f sec.)%n", id, elapsedTime.getAsDouble());
                cond.await();
                System.out.printf("%d : await OK! (%f sec.)%n", id, elapsedTime.getAsDouble());
            } catch (InterruptedException e) {
                System.out.println("InterruptedException!");
            } finally {
                lock.unlock();
            }
        });
    }

    TimeUnit.SECONDS.sleep(5);

    lock.lock();
    try {
        System.out.printf("signal all! (%f sec.)%n", elapsedTime.getAsDouble());
        cond.signalAll();
    } finally {
        lock.unlock();
    }
}

// 結果
// ↓
//1 : await ... (0.105841 sec.)
//2 : await ... (0.213281 sec.)
//3 : await ... (0.321265 sec.)
//signal all! (5.330418 sec.)
//1 : await OK! (5.330721 sec.)
//2 : await OK! (5.330926 sec.)
//3 : await OK! (5.331123 sec.)

signal メソッドを使う例です。

// 基準となる時刻
final long current = System.nanoTime();

// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedTime = () -> (System.nanoTime() - current) / 1000000000.0;

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 ... (%f sec.)%n", id, elapsedTime.getAsDouble());
                cond.await();
                System.out.printf("%d : await OK! (%f sec.)%n", id, elapsedTime.getAsDouble());
            } catch (InterruptedException e) {
                System.out.println("InterruptedException!");
            } finally {
                lock.unlock();
            }
        });
    }

    TimeUnit.SECONDS.sleep(5);

    for (int i = 0; i < 3; i++) {
        lock.lock();
        try {
            System.out.printf("signal! (%f sec.)%n", elapsedTime.getAsDouble());
            cond.signal();
        } finally {
            lock.unlock();
        }

        TimeUnit.SECONDS.sleep(2);
    }
}

// 結果
// ↓
//1 : await ... (0.105298 sec.)
//2 : await ... (0.214365 sec.)
//3 : await ... (0.324142 sec.)
//signal! (5.332321 sec.)
//1 : await OK! (5.332855 sec.)
//signal! (7.369622 sec.)
//2 : await OK! (7.369812 sec.)
//signal! (9.384503 sec.)
//3 : await OK! (9.384861 sec.)

関連記事

ページの先頭へ