第57回 ロック編 ReadWriteLockインターフェース
今回のテーマは「ReadWriteLock」です。
ReadWriteLockインターフェースを実装するReentrantReadWriteLockは読み込み用のLockと書き込み用のLockをサポートします。 読み込みロックはreadLockメソッドにより取得し、書き込みロックはwriteLockメソッドにより取得します。 このロックには以下の特徴があります。 読み込みロックは複数のスレッドが取得できる。 書き込みロックは1つのスレッドのみが取得できる。 読み込みロックの取得中は、他のスレッドは書き込みロックを取得できない。 書き込みロックの取得中は、他のスレッドは読み込みロックを取得できない。
また、ReentrantReadWriteLockのコンストラクタの引数は公平性を設定できます。 公平性がtrueに設定されると、lockメソッドを実行したスレッドがその実行順でロックを取得することを保証します。 しかし、書き込みロック待ちのスレッドがある場合、後続の読み込みスレッドはロックを取得できず、ロック待ちの状態となってしまうため、同時性の可能性を低下させる可能性があるのが注意点です。 公平性がfalseに設定されると、順序を保証しません。 しかし、書き込みロック待ちのスレッドがある場合でも、後続の読み込みスレッドがロックを取得できてしまうため、書き込みを遅らせる可能性があるのが注意点です。
次のサンプルコードは複数のスレッドから同時に要素の参照することを想定したリストクラスです。 データが入れられたあと、あまり変更されることなく頻繁に検索されるコレクションなどは、読み取り、書き込みロックに適しています。(J2SE5.0以上対応)
import java.util.LinkedList; import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class MyList { private List list = new LinkedList(); private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void add(E e) { readWriteLock.writeLock().lock(); try { list.add(e); } finally { readWriteLock.writeLock().unlock(); } } public E get(int index) { E e = null; readWriteLock.readLock().lock(); try { e = list.get(index); } finally { readWriteLock.readLock().unlock(); } return e; } public E remove(int index) { E e = null; readWriteLock.writeLock().lock(); try { e = list.remove(index); } finally { readWriteLock.writeLock().unlock(); } return e; } }
getメソッドは要素を参照するメソッドです。
同時に複数スレッドから参照するため、読み込みロックを使用しています。
addメソッドは要素を追加するメソッドで、removeメソッドは要素を削除するメソッドです。
リストの構造を変更する処理なので、書き込みロックを使用し、排他制御を行っています。