简体   繁体   English

并发问题 - 阻止队列

[英]Concurrency Issue - Blocking Queue

I am confused with concurrency - i am trying to stop the consumer thread from running if the producer is shutdown but am having issues if the consumer is blocked on take(). 我对并发性感到困惑 - 我试图阻止消费者线程运行,如果生产者关闭但是如果消费者在take()上被阻止则会出现问题。 I have tried adding a posion pill, interruptung the current thread, using a boolean flag and still to no avail. 我尝试添加一个posion药丸,使用布尔标志中断当前线程,但仍无济于事。

Please can someone help advise where I am going wrong. 请有人帮忙告诉我哪里出错了。 Thanks. 谢谢。

public class TestPoisonPill implements Runnable {
    private BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1);
    private volatile boolean stopped = false;

    public void addToQueue(String event) throws InterruptedException{
        System.out.println("in add to queue");
        if(event != null){
            try {
                queue.put(event);
            } catch (InterruptedException e) {
                stopped = true;
                queue.put("Poison");
                System.out.println("Unable to add the event to the queue, order routing processing is stopped");
                throw e;
            }
        }
    }

    @Override
    public void run() {
        while(!stopped){
            try {
                if(queue.size() > 0){
                    String string = queue.take();
                    System.out.println("taken " + string + "from the queue");
                }else{
                    continue;
                }
            }
            catch (InterruptedException e) {
                stopped = true;
            }
        }
    }

    public boolean isStopped(){
        return stopped;
    }

    protected BlockingQueue<String> getQueue() {
        return queue;
    }

    protected void setBoolean(boolean b){
        this.stopped = b;
    }

    public static void main(String[] args) throws InterruptedException{
        ExecutorService exec = Executors.newSingleThreadExecutor();
        final TestPoisonPill t = new TestPoisonPill();
        exec.execute(t);
        ExecutorService exec2 = Executors.newSingleThreadExecutor();
        Runnable addTask = new Runnable() {
            public void run() {
                while (true) {
                    try {
                        t.addToQueue("hi");
                        Thread.sleep(100);
                    } catch (InterruptedException ex) {
                        System.out.println("add task interrupted ");
                        t.setBoolean(true);
                        break;
                    }
                }
            }
        };
        exec2.execute(addTask);
        Thread.sleep(1000);
        exec2.shutdownNow();
    }
}

am confused with concurrency - i am trying to stop the consumer thread from running if the producer is shutdown but am having issues if the consumer is blocked on take() 与并发相混淆 - 我试图阻止消费者线程运行,如果生产者关闭但是如果消费者在take()上被阻止则会出现问题

If you problem is that you program is not stopping, I think you are missing an exec.shutdownNow() on your first ExecutorService . 如果您遇到的问题是程序没有停止,我认为您在第一个ExecutorService上缺少exec.shutdownNow() This will interrupt your first thread, if you change your loop to be something like: 如果您将循环更改为以下内容,这将中断您的第一个线程:

while (!stopped && !Thread.currentThread().isInterrupted()) {

Without the interrupt flag check any interrupt will be not been seen by the thread. 如果没有中断标志检查,线程将看不到任何中断。 An interrupt is just a flag that is set on the thread. 中断只是在线程上设置的标志。 Certain methods (like Thread.sleep(...) and BlockingQueue.take() ) throw InterruptedException when a thread is interrupted but your consumer is spinning and never calling take() . 某些方法(如Thread.sleep(...)BlockingQueue.take() )在线程被中断但您的消费者正在旋转并且从不调用take()时抛出InterruptedException

Really, the spin loop in the consumer is an extremely bad pattern. 真的,消费者的旋转循环是一个非常糟糕的模式。 It should just call queue.take() and then either use the interrupt or have your producer actually submit a poisoned pill. 它应该只调用queue.take() ,然后使用中断让你的生产者实际提交一个中毒的药丸。 Something like: 就像是:

while (!Thread.currentThread().isInterrupted()) {
    String string;
    try {
         string = queue.take();
    } catch (InterruptedException e) {
         break;
    }
    // here is where you could check for a poison pill
    // something like: if (string == STOP_PILL) break;
    System.out.println("taken " + string + "from the queue");
}

You don't really need the stopped flag if you are using interrupt appropriately. 如果你正确使用中断,你真的不需要stopped标志。

You mention having tried a "poisoned pill". 你提到尝试了“中毒丸”。 For others, a poisoned pill is when you put a specific "special" object on the queue which the consumer uses to know when to shutdown. 对于其他人来说,当你在队列中放置一个特定的“特殊”对象时,一个有毒的药丸是消费者用来知道何时关闭的。 Something like the following should work: 像下面这样的东西应该工作:

private static final String STOP_PILL = "__STOP_PLEASE!!__";
...
// the consumer removes from the queue
String string = queue.take();
// it tests to see if it a pill, == is better than .equals here
if (string == STOP_PILL) {
   // the consumer should stop
   break;
}
...
// to stop the consumer, the producer puts the pill into the queue
queue.put(STOP_PILL);

Lastly, you are using 2 ExecutorService instances when you could easily use one. 最后,当您可以轻松使用时,您正在使用2个ExecutorService实例。 I guess the point here is to interrupt only one of them but FYI. 我想这里的重点是只打断其中一个而不是FYI。 You can use a single Executors.newCachedThreadPool() which will create the number of threads you need. 您可以使用单个Executors.newCachedThreadPool()来创建所需的线程数。

你永远不关机的exec执行,只有exec2 ,所以线程中运行你的TestPoisonPill永远不会被打断。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM