JAVA合併-条件行列


JVMシリーズのブログでhttp://yizhenn.iteye.com/blog/2290864で話したことがあります
Java言語の同期メカニズムは、下の層では2つの手段しか実現されていません.「相互反発」と「協同」.Java言語のレベルでは、ロックと内蔵条件行列が内蔵されています.ロックすなわち、synchronizedを内蔵しています.この関連ブログは多くあります.これ以上話しません.
内蔵条件行列という言葉は聞いたことがないかもしれませんが、彼はObject.wait()、Object.notify()、Object.notifyAllの3つの方法を指しています.http://yizhenn.iteye.com/blog/2290864)なるほど!なぜ条件行列と言いますか?次に原因を説明します.
まず、行列はみんな知っています.先発的なデータ構造です.正常な列の中に保存されているのはすべて対象です.
条件付きキューには「待ち状態のスレッド」が格納されていますが、これらのスレッドはある特定の条件が真になるのを待っています.Javaオブジェクトごとに一つのロックとして使用できるように、各オブジェクトは同じ条件行列として、このオブジェクトのwait、notify、notifgAllは内部条件行列のAPIを構成している.オブジェクトの内蔵ロックは条件行列と関連しています.
条件行列を呼び出すためのいずれかの方法は、まずオブジェクトに対する鍵を持つ必要があります.わからなかったら何回も読んで、ゆっくり理解してください.
私達はすべて知っていて、スレッドは仕事のために用いたので、スレッドをずっと待っている状態にあることに人がいません.
したがって、「条件行列のスレッドは必ず実行できなくなり、待ち状態になる」ということです.この「実行できない条件」を「条件付き述語」といいます.
このようにして,3つの重要な概念を知った.ロック,条件付き述語,条件行列.彼らの関係は複雑ではないが、Wait()法の帰りは必ずしも待っている状態であるとは限らないので注意したい.列を上げる:
現在三つのスレッドが同じ条件で述語が真になるのを待っていると仮定し、他のスレッドがnotifyAll()法を起動した.この時、条件待ちのスレッドが一つしかありません.他のスレッドはまだ待ち状態になります.
コードの中でwhile(conditions is not true){this.wait]]を使ってif(condition id not true){this.wait()}を使わない理由.もう一つの場合は、同じ条件行列が複数の条件付き述語と関連付けられている.
このとき、この条件行列のnotifyAll()法を呼び出すと、ある条件付き述語は全く本物にならない.
本明細書の例では、ifではなくwhileを用いて条件付き述語が空であるかどうかを判断することが、上記のいくつかの理由に基づいて考慮されることが見られる.
私たちは一つの言葉で要約します.「スレッドがwaitから呼び覚まされるたびに、条件付きの述語を再度テストしなければなりません.」
void xxxMethod() throws InterruptedException{
	synchronized(lock){
		while(!conditionPredition)
			lock.wait();
		doSomething();
	}
}
まず、条件付きキューを使用しない例を見てみます.この例は境界のあるキャッシュを実現し、通過します.
ポーリングとスリープの形で簡単なブロックを実現します.
public abstract class BaseBoundedBuffer<T> {
	private Object[] buf ;
	private int head,tail,count;
	protected BaseBoundedBuffer(int capacity){
		this.head=0;
		this.tail=0;
		this.count=0;
		this.buf=new Object[capacity];
	}
	protected synchronized void doPut(T t){
		buf[tail]=t;
		if(++tail==buf.length)
			tail=0;
		++count;
	}
	protected synchronized T doGet(){
		Object obj=buf[head];
		buf[head]=null;
		if(++head==buf.length)
			head=0;
		--count;
		return (T)obj;
	}
	protected synchronized boolean isFull(){
		return count==buf.length;
	}
	protected synchronized boolean isEmpty(){
		return count==0;
	}
}


/**
 *                
 * @author Administrator
 *
 * @param <T>
 */
public class SleepyBoundedBuffer<T> extends BaseBoundedBuffer<T> {
	public SleepyBoundedBuffer(int capacity){
		super(capacity);
	}
	public void put(T t) throws InterruptedException{
		while(true){
			synchronized(this){
				if(!isFull()){
					doPut(t);
					return;
				}
			}
			Thread.sleep(2000);
		}
	}
	
	public T get() throws InterruptedException{
		while(true){
			synchronized (this) {
				if(!isEmpty()){
					return doGet();
				}
			}
			Thread.sleep(2000);
		}
	}

}
この例に対して、条件行列を使って、以下のように改善します.
public class BoundedBuffer<T> extends BaseBoundedBuffer<T> {
	public BoundedBuffer(int capacity){
		super(capacity);
	}
	public synchronized void put(T t) throws InterruptedException{
		while(!isFull())//             	!isFull	   
			this.wait();
		this.doPut(t);
		this.notifyAll();
	}
	public synchronized T get() throws InterruptedException{
		while(!isEmpty())//             	!isEmpty    
			this.wait();
		T t=this.doGet();
		this.notifyAll();
		return t;
	}
}
上記の例では、同じ条件の列に二つの条件付き述語が存在する.このように、notifyAllメソッドを呼び出すと、呼び覚まされるのはだけではない!isFullで待つスレッドは、まだあります!isEmpty上で待つスレッドは、起動にもかかわらず、
!isempty上で待つスレッドは必要ないので、より細かい粒度の条件行列を使いたいと迫る.Javaでは、内蔵ロックおよび内蔵条件行列の他に、明示的なロックおよび明示的条件行列が提供される.中には明示的なロックがロックされていますので、ブログを参照してください.http://yizhenn.iteye.com/blog/2303195ブログとhttp://yizhenn.iteye.com/blog/2303250.条件行列がCondationオブジェクトとして表示されます.
一つのCondationは一つのロックと関連しています.内蔵条件の列と一つの内蔵ロックが連結されているようです.Condationを作成するには、関連するLockでnewCondation()メソッドを呼び出すことができます.[color=red]各内蔵ロックには、これに関連した内蔵条件のキューしかないが、これとは違って、各ロックには彼と関連したCondationが複数あり、これによりCondationの制御がより細かくなります.上のBoundedBuffer類に対して、明示的条件行列を使って改善しています.
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionBoundedBuffer<T>  {
	private Lock lock=new ReentrantLock();
	private Condition notFull=lock.newCondition();
	private Condition notEmpty=lock.newCondition();
	
	private int head,tail,count;
	private Object[] buf;
	
	public ConditionBoundedBuffer(int capacity){
		buf=new Object[capacity];
		head=0;tail=0;count=0;
	}
	
	public void add(T t) throws InterruptedException{
		lock.lock();
		try{
			while(count==buf.length)
				notFull.wait();
			buf[tail]=t;
			if(++tail==buf.length)
				tail=0;
			count++;
			notEmpty.signal();
		}finally{
			lock.unlock();
		}
	}
	public T get() throws InterruptedException{
		lock.lock();
		try{
			while(count==0)
				notEmpty.wait();
			Object obj=buf[head];
			buf[head]=null;
			if(++head==buf.length)
				head=0;
			count--;
			notFull.signal();
			return (T)obj;
		}finally{
			lock.unlock();
		}
	}
}
特に注意してください.コンステレーションオブジェクトでは、wait、notify、notifgAllの3つの方法に対応するのは、await、signal、signal Allです.ただし、Javaにはすべての種類がObject類から継承されていますので、Condation類にもwait、notify、notifgAllがあります.注意してください.await、signal、signal Allを使う必要があります.
notifyとnotifyAll(signalとsignal Allと同じ)について、補足説明が必要です.notifyは、条件待ち行列からランダムにスレッドを選択して起動します.一方、notifyAllは、起動条件待ち行列のすべてのスレッドです.
次の二つの条件を同時に満たす場合のみ、notifyAllではなく単一のnotifyを使用することができます.条件1.すべての待ちスレッドタイプは同じである.一つの条件付き述語だけが条件付きキューに関連し、各スレッドがwaitから戻った後に同じ動作が行われる.条件2.単進単出:条件変数上の毎回の通知は、最大で一つのスレッドを起動して実行するしかない.