我的家園

          我的家園

          狀態依賴的類--JCIP C14.1讀書筆記

          Posted on 2012-04-15 16:26 zljpp 閱讀(112) 評論(0)  編輯  收藏

          [本文是我對Java Concurrency In Practice C14.1的歸納和總結. ?轉載請注明作者和出處, ?如有謬誤, 歡迎在評論中指正. ]?

          java類庫中包含許多狀態依賴的類: 其中的某些方法只有滿足特定的前置條件才能繼續, 比如BlockingQueue的take方法, 只有隊列不為空時take方法才能返回.

          狀態依賴的操作一般如下:

          void blockingAction() {
          	申請鎖
          	while(前置條件不滿足) {
          		釋放鎖
          		重新獲取鎖
          	}
          	執行操作
          	釋放鎖
          }
          ?

          BaseBoundedBuffer是一個普通抽象類, 它對put和take方法的實現是有缺陷的: 沒有在put方法執行前判斷緩沖區是否已滿, 也沒有在take方法執行之前判斷緩沖區是否為空. 其代碼如下:

          public abstract class BaseBoundedBuffer<V> {
          	private final V[] buf;
          	private int tail;
          	private int head;
          	private int count;
          
          	@SuppressWarnings("unchecked")
          	protected BaseBoundedBuffer(int capacity) {
          		this.buf = (V[]) new Object[capacity];
          	}
          
          	protected synchronized final void doPut(V v) {
          		buf[tail] = v;
          		if (++tail == buf.length) {
          			tail = 0;
          		}
          		++count;
          	}
          
          	protected synchronized final V doTake() {
          		V v = buf[head];
          		buf[head] = null;
          		if (++head == buf.length) {
          			head = 0;
          		}
          		--count;
          		return v;
          	}
          
          	public synchronized final boolean isFull() {
          		return count == buf.length;
          	}
          
          	public synchronized final boolean isEmpty() {
          		return count == 0;
          	}
          }?

          本文將使用多種方式為doPut和doTake方法增加前置判斷.

          ?

          通知調用方前置條件判斷失敗

          GrumpyBoundedBuffer是BaseBoundedBuffer的子類, 并向外提供put和take方法. 調用put方法時, 如果緩沖區已滿, 將拋出BufferFullException異常. 調用take方法時, 如果緩沖區為空, 將拋出BufferEmptyException異常:

          public class GrumpyBoundedBuffer<V> extends BaseBoundedBuffer<V> {
          	public GrumpyBoundedBuffer(int size) {
          		super(size);
          	}
          
          	public synchronized void put(V v) throws BufferFullException {
          		if (isFull())
          			throw new BufferFullException();
          		doPut(v);
          	}
          
          	public synchronized V take() throws BufferEmptyException {
          		if (isEmpty())
          			throw new BufferEmptyException();
          		return doTake();
          	}
          }?

          GrumpyBoundedBuffer實現起來很簡單, 但是這樣的類很難使用: 調用方需要捕獲并處理異常. 例如調用take方法時需要這樣:

          public void invocaton() {
          	while (true) {
          		try {
          			V item = buffer.take();
          			// use item 
          			break;
          		} catch (BufferEmptyException e) {
          			// 當拋出BufferEmptyException異常時, 說明buffer為空. 調用方睡眠一段時間后再進行嘗試
          			Thread.sleep(SLEEP_GRANULARITY);
          		}
          	}
          }?

          這樣的調用方式是令人難以忍受的, 而且sleep的時間SLEEP_GRANULARITY不好確定: 如果設定的太短, 將白白消耗CPU資源. 如果設定的太長, 則程序的響應性不好, 也有可能錯過前置條件滿足的時刻.

          ?

          內部處理重試邏輯

          既然由調用方處理異常并重試是不可取的, 那么SleepyBoundedBuffer類改為在內部處理重試邏輯:

          public class SleepyBoundedBuffer<V> extends BaseBoundedBuffer<V> {
          	private static final long SLEEP_GRANULARITY = 10;
          
          	public SleepyBoundedBuffer(int size) {
          		super(size);
          	}
          
          	public void put(V v) throws InterruptedException {
          		while (true) {
          			synchronized (this) {
          				if (!isFull()) {
          					doPut(v);
          					return;
          				}
          			}
          			// 釋放鎖后sleep一段時間再進行重試
          			Thread.sleep(SLEEP_GRANULARITY);
          		}
          	}
          
          	public V take() throws InterruptedException {
          		while (true) {
          			synchronized (this) {
          				if (!isEmpty()) {
          					return doTake();
          				}
          			}
          			// 釋放鎖后sleep一段時間再進行重試
          			Thread.sleep(SLEEP_GRANULARITY);
          		}
          	}
          }

          SleepyBoundedBuffer相比于GrumpyBoundedBuffer具有很大的進步: 不需要在調用方進行重試. SleepyBoundedBuffer易于使用, 但是sleep的時間仍然不好確定, 需要在響應性和CPU消耗間權衡.?

          ?

          前置條件滿足時喚醒線程

          BoundedBuffer試著解決SleepyBoundedBuffer中的問題: 當前置條件不滿足時將線程掛起, 并等待前置條件滿足時由其他線程喚醒, 這樣就不需要權衡sleep的時間了:

          public class BoundedBuffer<V> extends BaseBoundedBuffer<V> {
          	public BoundedBuffer(int size) {
          		super(size);
          	}
          
          	public synchronized void put(V v) throws InterruptedException {
          		// 當緩沖區已滿時將線程掛起, 等待其他線程喚醒
          		// 不給喚醒之后再次判斷緩沖區是否已滿
          		while (isFull()) {
          			wait();
          		}
          		doPut(v);
          		// 操作完成后喚醒其他線程
          		notifyAll();
          	}
          
          	public synchronized V take() throws InterruptedException {
          		// 當緩沖區為空時將線程掛起, 等待其他線程喚醒
          		// 被喚醒之后再次判斷緩沖區是否為空
          		while (isEmpty()) {
          			wait();
          		}
          		V v = doTake();
          		// 操作完成后喚醒其他線程
          		notifyAll();
          		return v;
          	}
          }?

          BoundedBuffer已經比較完美了, 相比于SleepyBoundedBuffer, 其具有更好的響應性, 更高的CPU效率以及更少的上下文切換.

          ?






          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 辛集市| 方山县| 独山县| 兰溪市| 大石桥市| 忻城县| 宜兰市| 罗田县| 萨迦县| 永清县| 河津市| 伊春市| 建瓯市| 兰考县| 新安县| 泰安市| 仙游县| 高密市| 九台市| 苏尼特右旗| 衡阳市| 桑植县| 仁化县| 杭锦后旗| 阿拉善盟| 武鸣县| 乐平市| 探索| 荥阳市| 福建省| 保亭| 利津县| 丘北县| 铜梁县| 广平县| 绍兴县| 五河县| 英德市| 辰溪县| 遂川县| 湘阴县|