我问这个关于控制从阻塞队列读取的线程的问题。尽管这不是我选择采用的解决方案,但有几个人建议将特殊的“毒丸”或“哨兵”值添加到队列中以将其关闭,如下所示:
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
一起使用。类似地,将虚拟值放入主构造函数只会传递这个问题,因为几个字段和构造函数参数本身就是重要的对象。
我是否只是过度防御性地编程?我是否应该担心向类中添加无参构造函数,即使没有设置器使对象可用(假设其他程序员足够明智,不会使用该构造函数)?如果 Foo
不能有一个无参构造函数或至少一个非值,它的设计是否会被破坏 - 最好将 if(someField == null){throw new RuntimeException();}
检查所有方法?
I was asking this question about controlling a thread that was reading from a blocking queue. Although it wasn't the solution I chose to go with, several people suggested that a special "poison pill" or "sentinel" value be added to the queue to shut it down like so:
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);
}
}
While I like this approach, I decided not to use it because I wasn't sure what value to use for the STOP
field. In some situations it's obvious - for instance, if you know you're inserting positive integers, negative numbers could be used as control values - but Foo
is a fairly complex class. It's immutable and hence has a constructor that takes several arguments. To add a no-argument constructor would mean leaving several fields uninitialised or null, which would cause methods to break if they were used elsewhere - Foo
is not just used with MyThread
. Similarly, putting dummy values into the main constructor would just pass this problem on as several of the fields and constructor parameters are themselves significant objects.
Am I simply programming over-defensively? Should I worry about adding no-argument constructors to a class, even if there are no setters to make the object usable (just assume other programmers will be sensible enough to not use that constructor)? Is the design of Foo
broken if it can't have a no-argument constructor or at least a non-value - would it be better to put if(someField == null){throw new RuntimeException();}
checks in all methods?
发布评论
评论(4)
我真的不明白这种设计与指示循环应该停止的简单布尔变量相比有什么优势。
但如果你真的想采用这种设计,我建议创建一个私有的无参数构造函数,并创建一个静态 STOP Foo。像这样。
这样做的优点是您仍然不会暴露无效的构造函数。
缺点是
Foo
类知道在某些情况下它被用作“毒丸”,但这可能不是它的用途。另一个缺点是STOP
对象可能不一致。您可以从中创建一个匿名子类,以禁用 UnsupportedOperationException 或其他方法的方法。I don't really see what the advantage of this design is versus a simple boolean variable to indicate the loop should stop.
But if you really want to go with this design, I would suggest making a private no-arg constructor, and making a static STOP Foo. Like this.
This has the advantage that you're still not exposing an invalid constructor.
The disadvantage is that the
Foo
class knows that in some cases it's used as a 'poison pill', which might not be what it's for. Another disadvantage is that TheSTOP
object might be inconsistent. You could make an anonymous subclass from it do disable the methods with UnsupportedOperationException or something.我认为您不使用空构造函数是正确的。如果 Foo 是一个如此复杂的类,那么为此使用完整的对象似乎不合逻辑。
如果可以添加
null
。这似乎是一个不错的方法。另一种方法也可以是实现接口。
IBlockableQueueObject
?这可以通过 foo 对象和 STOP 符号来实现。唯一的问题是,如果它不是 STOP,则必须将接口强制转换回 Foo。I think you're right about not using empty constructors. If Foo is such an complex class, it doesn't seem logical to use a complete object for that.
If adding a
null
is possible. That seems a nice way to go.Another way could also be to implement an interface.
IBlockableQueueObject
? This could be implemented by thefoo
object and by the STOP sign. Only thing is that you have to cast the interface back to theFoo
if it is not a STOP.另一种选择是将 Foo 包装在一个通用包装器中,如下所示:
然后您可以使用该包装器将 null 值作为毒丸传递给
BlockingQueue>
。another option would be to wrap Foo in a generic wrapper such as this:
which you can then use to pass a null value as a poison pill to a
BlockingQueue<Wrapped<Foo>>
.您应该担心无参构造函数不会产生可用的实例。
Foo 的设计听起来不错 - 我通常假设我不允许将 null 传递到构造函数中,除非文档明确允许我这样做。尤其是对于不可变的类。
You should worry about having no-argument constructors that don't result in usable instances.
The design of Foo sounds fine - I would generally assume that I'm not allowed to pass in null into a constructor unless the documentation specifically allows me to. Especially with an immutable class.