1. 概要

このチュートリアルでは、バイナリセマフォとリエントラントロックについて説明します。 また、それらを相互に比較して、一般的な状況に最も適しているものを確認します。

2. バイナリセマフォとは何ですか?

バイナリセマフォは、単一のリソースへのアクセスを介したシグナリングメカニズムを提供します。 言い換えると、バイナリセマフォは、一度に1つのスレッドのみがクリティカルセクションにアクセスできるようにする相互排除を提供します

そのため、アクセス可能な許可は1つだけ保持されます。 したがって、バイナリセマフォには2つの状態しかありません。1つの許可が利用可能であるか、0つの許可が利用可能です

Javaで利用可能なセマフォクラスを使用したバイナリセマフォの簡単な実装について説明しましょう。

Semaphore binarySemaphore = new Semaphore(1);
try {
    binarySemaphore.acquire();
    assertEquals(0, binarySemaphore.availablePermits());
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    binarySemaphore.release();
    assertEquals(1, binarySemaphore.availablePermits());
}

ここで、acquireメソッドが使用可能な許可を1つ減らすことがわかります。 同様に、 release メソッドは、使用可能な許可を1つ増やします。

さらに、Semaphoreクラスはfairnessパラメーターを提供します。 true に設定すると、 fairness パラメーターは、要求元のスレッドが許可を取得する順序(待機時間に基づく)を保証します。

Semaphore binarySemaphore = new Semaphore(1, true);

3. リエントラントロックとは何ですか?

リエントラントロックは相互排除メカニズムであり、は、デッドロック状態なしでスレッドがリソースのロックに(複数回)再入できるようにします。

ロックに入るスレッドは、ホールドカウントを毎回1つ増やします。 同様に、ロック解除が要求されると、ホールドカウントが減少します。 したがって、リソースは、カウンターがゼロに戻るまでロックされます。

たとえば、Javaで利用可能なReentrantLockクラスを使用した簡単な実装を見てみましょう。

ReentrantLock reentrantLock = new ReentrantLock();
try {
    reentrantLock.lock();
    assertEquals(1, reentrantLock.getHoldCount());
    assertEquals(true, reentrantLock.isLocked());
} finally {
    reentrantLock.unlock();
    assertEquals(0, reentrantLock.getHoldCount());
    assertEquals(false, reentrantLock.isLocked());
}

ここでは、 ロック メソッドは、ホールドカウントを1つ増やし、リソースをロックします。 同様に、 ロックを解除する メソッドは、ホールドカウントを減らし、ホールドカウントがゼロの場合はリソースのロックを解除します。

スレッドがロックに再び入るとき、リソースを解放するために同じ回数ロック解除を要求する必要があります。

reentrantLock.lock();
reentrantLock.lock();
assertEquals(2, reentrantLock.getHoldCount());
assertEquals(true, reentrantLock.isLocked());

reentrantLock.unlock();
assertEquals(1, reentrantLock.getHoldCount());
assertEquals(true, reentrantLock.isLocked());

reentrantLock.unlock();
assertEquals(0, reentrantLock.getHoldCount());
assertEquals(false, reentrantLock.isLocked());

Semaphore クラスと同様に、ReentrantLockクラスもfairnessパラメーターをサポートします。

ReentrantLock reentrantLock = new ReentrantLock(true);

4. バイナリセマフォと リエントラントロック

4.1. 機構

バイナリセマフォは一種のシグナリングメカニズムですが、リエントラントロックはロックメカニズムです。

4.2. 所有

バイナリセマフォの所有者であるスレッドはありません。 ただし、リソースを正常にロックした最後のスレッドは、再入可能ロックの所有者です。

4.3. 自然

バイナリセマフォは本質的に再入可能ではなく、同じスレッドがクリティカルセクションを再取得できないことを意味します。そうしないと、デッドロック状態になります。

一方、リエントラントロックは、本質的に、同じスレッドで複数回ロックに再入することを許可します。

4.4. 柔軟性

バイナリセマフォは、ロックメカニズムとデッドロック回復のカスタム実装を可能にすることにより、より高いレベルの同期メカニズムを提供します。 したがって、開発者により多くの制御を与えることができます。

ただし、リエントラントロックは、固定ロックメカニズムとの低レベルの同期です。

4.5. 変形

バイナリセマフォは、待機やシグナリング(Javaのセマフォクラスの場合は取得および解放)などの操作をサポートして、任意のプロセスで使用可能な許可を変更できるようにします。

一方、リソースをロック/ロック解除したのと同じスレッドのみが、再入可能ロックを変更できます。

4.6. デッドロック回復

バイナリセマフォは、非所有権解放メカニズムを提供します。 したがって、どのスレッドでも、バイナリセマフォのデッドロック回復の許可を解放できます。

逆に、リエントラントロックの場合、デッドロックの回復を実現することは困難です。 たとえば、再入可能ロックの所有者スレッドがスリープ状態になるか、無限に待機すると、リソースを解放できなくなり、デッドロック状態が発生します。

5. 結論

この短い記事では、バイナリセマフォとリエントラントロックについて説明しました。

最初に、Javaでの基本的な実装とともに、バイナリセマフォとリエントラントロックの基本的な定義について説明しました。 次に、メカニズム、所有権、柔軟性などのいくつかのパラメーターに基づいて、それらを相互に比較しました。

バイナリセマフォは、相互排除のための非所有権ベースのシグナリングメカニズムを提供すると確かに結論付けることができます。 同時に、デッドロックの回復が容易なロック機能を提供するためにさらに拡張することができます。

一方、リエントラントロックは、所有者ベースのロック機能を備えたリエントラント相互排除を提供し、単純なミューテックスとして役立ちます。

いつものように、ソースコードはGitHubから入手できます。