Java &引用;结案;阻塞队列

Java &引用;结案;阻塞队列,java,multithreading,blockingqueue,Java,Multithreading,Blockingqueue,我在一个非常简单的生产者-消费者场景中使用java.util.concurrent.BlockingQueue。例如,该伪代码描述了消费者部分: class QueueConsumer implements Runnable { @Override public void run() { while(true) { try { ComplexObject complexObject = my

我在一个非常简单的生产者-消费者场景中使用java.util.concurrent.BlockingQueue。例如,该伪代码描述了消费者部分:

class QueueConsumer implements Runnable {

    @Override
    public void run() {
        while(true)
        {
            try {
                ComplexObject complexObject = myBlockingQueue.take();
                //do something with the complex object
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}
到目前为止还不错。在阻塞队列的javadoc中,我读到:

BlockingQueue在本质上并不存在 支持任何类型的“关闭”或 “关机”操作,以指示 不会再添加任何项目。需求 而这些特性的使用往往是 依赖于实现。例如 一种常见的策略是让生产商 插入流或毒药的特殊末端 被解释的对象 当消费者采取相应措施时

不幸的是,由于使用的泛型和ComplexObject的性质,将“有毒对象”推到队列中并不容易。所以这种“常用策略”在我的场景中并不方便

我的问题是:我还可以使用什么好的策略/模式来“关闭”队列


谢谢大家!

如果您有消费者线程的句柄,可以中断它。使用您提供的代码,这将杀死消费者。我不希望制作人有这个;它可能必须以某种方式回调程序控制器,让它知道它已经完成了。然后控制器将中断使用者线程

你总是可以在服从打断之前完成工作。例如:

class QueueConsumer implements Runnable {
    @Override
    public void run() {
        while(!(Thread.currentThread().isInterrupted())) {
            try {
                final ComplexObject complexObject = myBlockingQueue.take();
                this.process(complexObject);

            } catch (InterruptedException e) {
                // Set interrupted flag.
                Thread.currentThread().interrupt();
            }
        }

        // Thread is getting ready to die, but first,
        // drain remaining elements on the queue and process them.
        final LinkedList<ComplexObject> remainingObjects;
        myBlockingQueue.drainTo(remainingObjects);
        for(ComplexObject complexObject : remainingObjects) {
            this.process(complexObject);
        }
    }

    private void process(final ComplexObject complexObject) {
        // Do something with the complex object.
    }
}
类QueueConsumer实现可运行{
@凌驾
公开募捐{
而(!(Thread.currentThread().isInterrupted()){
试一试{
final ComplexObject ComplexObject=myBlockingQueue.take();
这个过程(complexObject);
}捕捉(中断异常e){
//设置中断标志。
Thread.currentThread().interrupt();
}
}
//线程即将死亡,但首先,
//排空队列上的剩余元素并对其进行处理。
最终LinkedList保留的对象;
myBlockingQueue.drainTo(剩余对象);
对于(ComplexObject ComplexObject:RemainingObject){
这个过程(complexObject);
}
}
私有无效进程(最终ComplexObject ComplexObject){
//对复杂的对象执行一些操作。
}
}
事实上,我更愿意这样做,而不是以某种方式毒害队列。如果您想杀死线程,请让线程杀死自己

(很高兴看到有人正确地处理了
InterruptedException


这里似乎有一些关于中断处理的争论。首先,我希望每个人都能阅读

现在,在没有人真正读到的情况下,我们达成协议。如果线程在中断时当前处于阻塞状态,那么它将只接收到一个
InterruptedException
。在这种情况下,
Thread.interrupted()
将返回
false
。如果它没有阻塞,它将不会收到此异常,相反,
Thread.interrupted()
将返回
true
。因此,无论发生什么情况,您的循环保护都应该绝对地检查
Thread.interrupted()
,否则就有错过线程中断的风险

因此,由于您正在检查
Thread.interrupted()
,无论发生什么情况,您都必须捕获
InterruptedException
(即使您没有被强制,也应该处理它),因此您现在有两个代码区域来处理相同的事件,即线程中断。处理这种情况的一种方法是将它们规范化为一个条件,这意味着布尔状态检查可以引发异常,或者异常可以设置布尔状态。我选择后者



编辑:请注意,静态线程中断方法会清除当前线程的中断状态。

是否可以扩展ComplexObject并模拟非平凡的创建功能?从本质上讲,您最终得到的是一个shell对象,但您可以执行
实例来查看是否是队列的结尾对象。

在这种情况下,您通常必须放弃泛型,并使队列保持类型为对象。然后,在强制转换为实际类型之前,您只需检查“毒药”对象。

制作毒药对象的另一种可能性:将其作为类的特定实例。这样,您就不必到处乱搞子类型或搞砸您的泛型

缺点:如果生产者和消费者之间存在某种序列化障碍,这将不起作用

public class ComplexObject
{
    public static final POISON_INSTANCE = new ComplexObject();

    public ComplexObject(whatever arguments) {
    }

    // Empty constructor for creating poison instance.
    private ComplexObject() {
    }
}

class QueueConsumer implements Runnable {
    @Override
    public void run() {
        while(!(Thread.currentThread().interrupted())) {
            try {
                final ComplexObject complexObject = myBlockingQueue.take();
                if (complexObject == ComplexObject.POISON_INSTANCE)
                    return;

                // Process complex object.

            } catch (InterruptedException e) {
                // Set interrupted flag.
                Thread.currentThread().interrupt();
            }
        }
    }
}

另一种方法是使用包装正在进行的处理,并让
ExecutorService
本身控制是否将作业添加到队列中

基本上,您可以利用,当调用它时,将禁止执行者处理更多的任务

在您的示例中,我不确定您当前如何将任务提交给
QueueConsumer
。我假设您有某种
submit()
方法,并在示例中使用了类似的方法

import java.util.concurrent.*;

class QueueConsumer {
    private final ExecutorService executor = Executors.newSingleThreadExecutor();

    public void shutdown() {
        executor.shutdown(); // gracefully shuts down the executor
    }

    // 'Result' is a class you'll have to write yourself, if you want.
    // If you don't need to provide a result, you can just Runnable
    // instead of Callable.
    public Future<Result> submit(final ComplexObject complexObject) {
        if(executor.isShutdown()) {
            // handle submitted tasks after the executor has been told to shutdown
        }

        return executor.submit(new Callable<Result>() {
            @Override
            public Result call() {
                return process(complexObject);
            }
        });
    }

    private Result process(final ComplexObject complexObject) {
        // Do something with the complex object.
    }
}
import java.util.concurrent.*;
类排队消费者{
private final executor service executor=Executors.newSingleThreadExecutor();
公共空间关闭(){
executor.shutdown();//优雅地关闭执行器
}
//“Result”是一个你必须自己编写的类,如果你愿意的话。
//如果不需要提供结果,只需Runnable即可
//而不是可调用的。
公共未来提交(最终ComplexObject ComplexObject){
if(executor.isShutdown()){
//在告知执行者关闭后处理提交的任务
}
返回执行器。提交(新的可调用(){
@凌驾
公共结果调用(){
返回过程(complexObject);
}
});
}
私有结果处理(最终ComplexObject ComplexObject){
//对复杂的对象执行一些操作。
}
}
这个
class ComplexObject implements QueueableComplexObject
{
    /* the meat of your complex object is here as before, just need to
     * add the following line and the "implements" clause above
     */
    @Override public ComplexObject asComplexObject() { return this; }
}

enum NullComplexObject implements QueueableComplexObject
{
    INSTANCE;

    @Override public ComplexObject asComplexObject() { return null; }
}

interface QueueableComplexObject
{
    public ComplexObject asComplexObject();
}
boolean ok = true;
while (ok)
{
    ComplexObject obj = queue.take().asComplexObject();
    if (obj == null)
        ok = false;
    else
        process(obj);
}

/* interrupt handling elided: implement this as you see fit,
 * depending on whether you watch to swallow interrupts or propagate them
 * as in your original post
 */
import java.util.concurrent.BlockingQueue;

public interface CloseableBlockingQueue<E> extends BlockingQueue<E> {
    /** Returns <tt>true</tt> if this queue is closed, <tt>false</tt> otherwise. */
    public boolean isClosed();

    /** Closes this queue; elements cannot be added to a closed queue. **/
    public void close();
}
ConsumerClass
private boolean queueIsNotEmpty = true;//with setter
...
do {
    ...
    sharedQueue.drainTo(docs);
    ...
} while (queueIsNotEmpty || sharedQueue.isEmpty());
public class ComplexObjectWrapper {
ComplexObject obj;
}

public class EndOfQueue extends ComplexObjectWrapper{}