Java
 Computer >> コンピューター >  >> プログラミング >> Java

JavaのReentrantLockとsynchronizedの違いを徹底比較!同期処理の使い分けポイント

マルチスレッド環境で複数のスレッドから共有リソースへ安全にアクセスするためには、適切なロック機構が必要です。Javaには主に2つの方法があります。1つはReentrantLock(またはReadWriteLock)を使用する方法、もう1つはsynchronizedキーワードを使用する方法です。

ReentrantLockとは

ReentrantLockクラスは、Java 5以降のjava.util.concurrentパッケージで提供されているクラスで、Lockインターフェースの実装です。公式ドキュメント(Javadoc)によると、Lockインターフェースの実装は、synchronizedメソッドやブロックでは実現できない、より柔軟で高度なロック操作を提供します。

ReentrantLockとsynchronizedの違い一覧

No.項目ReentrantLocksynchronized
1 ロックの取得 lock()メソッドを呼び出して明示的にロックを取得する synchronizedキーワードを記述するだけでロックを取得できる
2 ロックの解放 プログラマがunlock()メソッドを明示的に呼び出す必要がある ブロックを抜ける際に暗黙的に自動的に解放される
3 割り込みの可否 lockInterruptibly()メソッドを使えば、ロック待ち中のスレッドに割り込み可能 ロック待ち中のスレッドに割り込むことはできない
4 公平性(フェアネス) コンストラクタで公平性パラメータを指定可能。trueを設定すると、最も長く待機しているスレッドが優先的にロックを獲得できる 特定のアクセス順序は一切保証されない
5 ロック解放の順序 任意の順序でロックを解放できる 取得した順序と同じ順序で解放する必要がある

ReentrantLockのコード例

ReentrantLockを使う場合の基本的な書き方は次のとおりです。ロックの取得はtryブロックの前に行い、解放は必ずfinallyブロック内で行うのが定石です。これにより、例外が発生してもロックが確実に解放されます。

public class ReentrantLockExample implements Runnable {
    private final Lock lock = new ReentrantLock();

    @Override
    public void run() {
        lock.lock();
        try {
            // 共有リソースに対する排他処理
        } finally {
            lock.unlock();
        }
    }
}

また、lockInterruptibly()を使えば、ロックの取得待ちの間に他スレッドからの割り込みを受け付けることもできます。

lock.lockInterruptibly(); // 待ち時間中も割り込み可能
try {
    // 共有リソースに対する排他処理
} finally {
    lock.unlock();
}

synchronizedのコード例

一方、synchronizedは言語仕様として組み込まれた仕組みのため、記述が非常にシンプルです。ロックの取得と解放は自動的に行われるため、unlock()の呼び忘れによるデッドロックの心配がありません。

public class SynchronizedLockExample implements Runnable {
    private final Object resource = new Object();

    @Override
    public void run() {
        synchronized (resource) {
            // 共有リソースに対する排他処理
        }
    }
}

どちらを使うべきか?

単純な排他制御で十分なケースでは、シンプルで安全なsynchronizedを選ぶのが基本です。一方、以下のような高度な制御が必要な場合にはReentrantLockが適しています。

  • ロック待ち中のスレッドに割り込みをかけたい場合(lockInterruptibly())
  • 一定時間だけロック取得を試みたい場合(tryLock())
  • 公平性のあるロック(先に待っていたスレッドを優先)を実現したい場合
  • 複数の条件変数(Condition)を使ったきめ細かなスレッド間通信が必要な場合

用途に応じて両者を使い分けることで、安全かつ効率的なマルチスレッドプログラミングが実現できます。

  1. JavaのIteratorとEnumerationの違いを徹底比較!使い分けのポイントを解説

    はじめに:IteratorとEnumerationとはJavaのコレクションフレームワークには、コレクション内の要素を順番に走査(トラバース)しながらアクセスするための仕組みとして「カーソル」が用意されています。その代表格が Iterator(イテレータ) と Enumeration(列挙) の2つです。両者はどちらもコレクションフレームワークに属しますが、登場した時期と役割が異なります。EnumerationはJDK 1.0から存在する歴史あるインターフェースで、IteratorはJDK 1.2でコレクションフレームワークとともに導入された、より新しいインターフェースです。Enumerati

  2. JavaでのArrayListとHashSetの違いを徹底解説!使い分けのポイントも紹介

    HashSetとArrayListは、どちらもJavaコレクションフレームワークにおいて最も重要なクラスの一つです。それぞれ特徴が異なるため、用途に応じた適切な使い分けが求められます。本記事では、ArrayListとHashSetの主な違いを6つの観点から比較し、実際のサンプルコードとともにわかりやすく解説します。ArrayListとHashSetの違い一覧No.項目ArrayListHashSet1実装インターフェースListインターフェースを実装しています。Setインターフェースを実装しています。2内部構造内部的には配列(動的配列)を使って実装されています。内部的にはHashMapを使って