我问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
是一个相当复杂的类。它是不可变的,因此有一个带有几个参数的构造函数。添加无参数构造函数意味着将几个字段保留为未初始化或为null,这会导致方法在其他地方使用时中断 - Foo
不仅仅与MyThread
一起使用。类似地,将虚拟值放入主构造函数只会传递此问题,因为几个字段和构造函数参数本身就是重要对象。
我只是编程过于防守吗?我是否应该担心在类中添加无参数构造函数,即使没有setter使对象可用(只是假设其他程序员足够明智而不使用该构造函数)?如果Foo
的设计没有无参数构造函数或者至少是非值,那么它是否会被破坏 - 在所有方法中放置if(someField == null){throw new RuntimeException();}
检查会更好吗?
答案 0 :(得分: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 :(得分:1)
我认为你不使用空构造函数是正确的。如果Foo是一个如此复杂的类,那么使用完整的对象似乎不合逻辑。
如果可以添加null
。这似乎是一个很好的方式。
另一种方法也可以是实现接口。 IBlockableQueueObject
?这可以通过foo
对象和STOP符号来实现。唯一的问题是,如果它不是STOP,你必须将界面强制转换回Foo
。
答案 2 :(得分:1)
另一种选择是将Foo包装在通用包装器中,例如:
public class Wrapped<T> {
private final T value;
public Wrapped(T value) {
this.value = value;
}
public T get() { return value; }
}
然后您可以使用它将空值作为毒丸传递给BlockingQueue<Wrapped<Foo>>
。
答案 3 :(得分:0)
你应该担心没有导致可用实例的无参数构造函数。
Foo的设计听起来很好 - 我通常会假设我不允许将null传入构造函数,除非文档特别允许我这样做。特别是有一个不可变的类。