EeBlog(テクニカルブログ)

第55回 ロック編 Lockインターフェース

今回のテーマは「Lock」です。

synchronizedによるオブジェクトのロックのほかに、Lockインターフェースを利用し、より柔軟な排他制御を行うことができます。 Lockインターフェースを実装するクラスにはReentrantLockなどがあります。 lockメソッドによりロックを取得し、unlockメソッドによりロックを開放します。 通常はlockメソッドの実行直後にtryブロックを続け、finallyブロックでunlockメソッドを実行します。

次のサンプルコードは数値を格納するリストクラスです。 内部で奇数と偶数を区別して保持します。(J2SE5.0以上対応)

import java.util.LinkedList; 
import java.util.List; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock;

public class NumberList {

    private List<Integer> oddList = new LinkedList<Integer>(); 
    private List<Integer> evenList = new LinkedList<Integer>(); 
    private Lock oddLock = new ReentrantLock(); 
    private Lock evenLock = new ReentrantLock();

    public void add(int number) { 
        if (Math.abs(number % 2) == 1) { 
            addOdd(number); 
        } else { 
            addEven(number); 
        } 
    }

    private void addOdd(int number) { 
        oddLock.lock(); 
        try { 
            oddList.add(number); 
        } finally { 
            oddLock.unlock(); 
        } 
    }

    private void addEven(int number) { 
        evenLock.lock(); 
        try { 
            evenList.add(number); 
        } finally { 
            evenLock.unlock(); 
        } 
    }
}

NumberListクラスは複数のスレッドから数値が追加されることを想定しています。
数値の追加にはaddメソッドを実行します。
引数の数値が奇数ならaddOddメソッドを実行し、偶数ならaddEvenメソッドを実行し、奇数と偶数を別々のリストに格納します。
これらのメソッドは複数のスレッドから実行される可能性があるので、リストに対する数値の追加操作には排他制御が必要です。
しかし、オブジェクトのロックは1つしかないため、addOddメソッドとaddEvenメソッドをsynchronizedメソッドにした場合、奇数の追加操作と偶数の追加操作は排他的になります。
奇数を格納するリストと偶数を格納するリストを区別しているため、この2つの操作が排他的である必要はありません。
そこで、奇数の追加操作と偶数の追加操作で別々のLockインスタンスを使用し、この2つの操作を非同期で行えるようにしています。