JAva同時locksパッケージ:Condition(二)


本稿では基本的にJDK文書の説明内容であるが,Conditionオブジェクトの使い方はよく説明されている.
 
Conditionは、Objectモニタメソッド(wait、notify、notifyAll)を異なるオブジェクトに分解し、これらのオブジェクトを任意のLockと組み合わせて使用することで、各オブジェクトに複数の待機set(wait-set)を提供する.ここで、Lockはsynchronizedメソッドと文の使用を代替し、ConditionはObjectモニタメソッドの使用を代替する.
 
条件(条件キューまたは条件変数とも呼ばれる)は、ある状態条件がtrueの別のスレッドに通知される前にスレッドを保留する(すなわち、スレッドを「待機」させる)ための意味をスレッドに提供します.).この共有ステータス情報にアクセスするには、異なるスレッドで発生するため、保護される必要があります.そのため、条件に何らかのロックを関連付ける必要があります.条件の提供を待つ主な属性は、関連するロックを原子的に解放し、Objectのように現在のスレッドを掛けることである.waitがやったように. 
 
Conditionインスタンスは実質的にロックにバインドされます.特定のロックインスタンスに対してConditionインスタンスを取得するには、そのnewCondition()メソッドを使用します. 
 
一例として、putメソッドとtakeメソッドをサポートするバインドされたバッファがあると仮定する.空のバッファでtake操作を実行しようとすると、あるアイテムが使用可能になるまでスレッドがブロックされます.フルバッファでput操作を実行しようとすると、スペースが利用可能になるまでスレッドがブロックされます.putスレッドとtakeスレッドを個別の待機setに保存することで、バッファ内のアイテムやスペースが利用可能になったときに最適な計画を利用して、一度に1つのスレッドだけを通知することができます.これは、2つのConditionインスタンスを使用して行うことができます. 
 
コードの例:
package com.mutex;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *          
 */
public class MyBlockingQueue<T> {

	private final Lock lock = new ReentrantLock();

	private Condition notFull = lock.newCondition();
	private Condition notEmpty = lock.newCondition();

	private final Object[] items = new Object[100];
	private int putptr, takeptr, count;

	public void put(T x) {
		lock.lock();
		try {
			while (count == items.length)
				notFull.await();

			items[putptr] = x;
			if (++putptr == items.length)
				putptr = 0;

			++count;

			notEmpty.signal();

		} catch (InterruptedException e) {
			// todo
		} finally {
			lock.unlock();
		}
	}

	public T take() {
		lock.lock();
		try {
			while (count == 0)
				notEmpty.await();

			T x = (T) items[takeptr];
			if (++takeptr == items.length)
				takeptr = 0;

			--count;

			notFull.signal();

			return x;
		} catch (InterruptedException e) {
			// todo
			return null;
		} finally {
			lock.unlock();
		}

	}

	public static void main(String[] args) {

		final MyBlockingQueue<String> bq = new MyBlockingQueue<String>();

		new Thread() {
			public void run() {
				int count = 0;
				while (true) {
					++count;
					bq.put("test: " + count);
				}
			}
		}.start();

		new Thread() {
			public void run() {
				while (true) {
					System.out.println(bq.take());
				}
			}
		}.start();
	}

}