2011-01-20 39 views
2

我在問this有關控制從阻塞隊列讀取的線程的問題。雖然這不是我選擇去與解決方案,一些人建議,一個特殊的「毒丸」或「前哨」值添加到隊列關閉它,像這樣:雖然我喜歡無值的對象

public class MyThread extends Thread{ 
    private static final Foo STOP = new Foo(); 
    private BlockingQueue<Foo> blockingQueue = new LinkedBlockingQueue<Foo>(); 

    public void run(){ 
     try{ 
      Foo f = blockingQueue.take(); 
      while(f != STOP){ 
       doSomethingWith(f); 
       f = blockingQueue.take(); 
      } 
     } 
     catch(InterruptedException e){ 

     } 
    } 

    public void addToQueue(Foo f) throws InterruptedException{ 
     blockingQueue.put(f); 
    } 

    public void stop() throws InterruptedException{ 
     blockingQueue.put(STOP); 
    } 
} 

這種做法,我決定不使用它,因爲我不確定STOP字段使用什麼值。在某些情況下,很明顯 - 例如,如果您知道插入正整數,負數可以用作控制值 - 但Foo是一個相當複雜的類。它是不可變的,因此有一個構造函數需要幾個參數。要添加無參數構造函數意味着將幾個字段保留爲未初始化或爲空,這會導致方法在其他地方使用時中斷 - Foo不僅僅用於MyThread。同樣,將虛擬值放入主構造函數中只會傳遞此問題,因爲幾個字段和構造函數參數本身都是重要的對象。

我只是簡單的過防守編程嗎?我應該擔心向類中添加無參數構造函數,即使沒有setter來使對象可用(假定其他程序員將足夠理智而不使用該構造函數)?如果Foo的設計不能有無參數的構造函數或者至少有一個非值 - 那麼Foo的設計是否會被破壞?在所有方法中放入if(someField == null){throw new RuntimeException();}檢查會更好嗎?

回答

1

我認爲你是正確的不使用空的構造函數。如果Foo是一個複雜的類,那麼爲它使用完整的對象似乎不合邏輯。

如果添加null是可能的。這似乎是一個好方法。

另一種方式也可以是實現一個接口。 IBlockableQueueObject?這可以通過foo對象和STOP標誌來實現。唯一的問題是,如果它不是STOP,那麼你必須將接口轉換回Foo

0

應該擔心沒有參數的構造函數不會導致可用的實例。

Foo的設計聽起來很好 - 我通常會假設我不允許將null傳遞給構造函數,除非文檔特別允許。尤其是對於一個不可變的類。

2

我真的不知道這個設計的優點是什麼,而不是一個簡單的布爾變量來表明循環應該停止。但如果你真的想用這個設計,我會建議製作一個私有的無參數構造函數,並且創建一個靜態的STOP Foo。喜歡這個。

public class Foo { 

    public static final Foo STOP = new Foo(); 
    ... fields 

    private Foo(){} 
    public Foo(...){ 
    ... 
    } 

    ... 

} 

public class MyThread extends Thread{ 
    private static final Foo STOP = new Foo(); 
    private BlockingQueue<Foo> blockingQueue = new LinkedBlockingQueue<Foo>(); 

    public void run(){ 
     try{ 
      Foo f = blockingQueue.take(); 
      while(f != STOP){ 
       doSomethingWith(f); 
       f = blockingQueue.take(); 
      } 
     } 
     catch(InterruptedException e){ 

     } 
    } 

    public void addToQueue(Foo f) throws InterruptedException{ 
     blockingQueue.put(f); 
    } 

    public void stop() throws InterruptedException{ 
     blockingQueue.put(Foo.STOP); 
    } 
} 

這樣做的好處是您仍然沒有公開無效的構造函數。

缺點是Foo類知道在某些情況下,它被用作'毒丸',它可能不是它的用途。另一個缺點是STOP對象可能不一致。你可以通過它創建一個匿名的子類來禁用UnsupportedOperationException之類的方法。

1

另一種選擇將是一個通用的包裝來包裝美孚像這樣:

public class Wrapped<T> { 
    private final T value; 

    public Wrapped(T value) { 
     this.value = value; 
    } 

    public T get() { return value; } 
} 

然後您可以用它來傳遞一個空值作爲一個毒丸計劃到BlockingQueue<Wrapped<Foo>>