Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/393.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java BlockingQueue在take()上阻塞,稍微有点扭曲_Java_Concurrency_Blockingqueue - Fatal编程技术网

Java BlockingQueue在take()上阻塞,稍微有点扭曲

Java BlockingQueue在take()上阻塞,稍微有点扭曲,java,concurrency,blockingqueue,Java,Concurrency,Blockingqueue,我有两个阻塞队列的情况。首先,我插入一些我执行的任务。当每个任务完成时,它会将一个任务添加到第二个队列中,在那里执行任务 所以我的第一个队列很简单:我只是检查确保它不是空的,然后执行,否则我会中断() 我做的第二件事,正如你所知,是行不通的: public void run() { try { SomeTask2 task2 = taskQueue2.take(); doTask(task2); } catch (InterruptedE

我有两个阻塞队列的情况。首先,我插入一些我执行的任务。当每个任务完成时,它会将一个任务添加到第二个队列中,在那里执行任务

所以我的第一个队列很简单:我只是检查确保它不是空的,然后执行,否则我会中断()

我做的第二件事,正如你所知,是行不通的:

public void run() {
    try {
        SomeTask2 task2 = taskQueue2.take();
        doTask(task2);
    }

    catch (InterruptedException ex) {

    }
    Thread.currentThread().interrupt();

}
如何解决该问题,使第二个BlockingQueue不会在take()上阻塞,但只有在它知道没有更多项要添加时才会完成。如果第二个线程可以看到第一个阻塞队列,并检查该队列是否为空,第二个队列是否也为空,那么它将中断

我也可以使用有毒物品,但我更喜欢其他东西


注意:这不是确切的代码,只是我在这里写的一些东西:

我不知道您在这里实际想做什么,但我可以说,您的第一个
run()
方法中的
interrupt()
要么毫无意义,要么是错误的

  • 如果您正在自己的
    Thread
    对象中运行
    run()
    方法,那么该线程无论如何都将退出,因此没有必要中断它

  • 如果在具有线程池的执行器中运行
    run()
    方法,则很可能根本不想终止线程或关闭执行器。。。在那一点上。如果确实要关闭执行器,则应调用其关闭方法之一


例如,这里有一个版本,没有所有的中断内容,也没有线程创建/破坏搅动,你看起来在做什么

public class TaskExecutor {

    private ExecutorService executor = new ThreadPoolExecutorService(...);

    public void submitTask1(final SomeTask task) {
        executor.submit(new Runnable(){
            public void run() {
                doTask(task);
                submitTask2(task);
            }
        });
    }

    public void submitTask2(final SomeTask task) {
        executor.submit(new Runnable(){
            public void run() {
                doTask2(task);
            }
        });
    }

    public void shutdown() {
        executor.shutdown();
    }
 }

如果要为任务单独排队,只需创建并使用两个不同的执行器即可

我不知道您在这里实际想做什么,但我可以说,您的第一个
run()
方法中的
interrupt()
要么毫无意义,要么是错误的

  • 如果您正在自己的
    Thread
    对象中运行
    run()
    方法,那么该线程无论如何都将退出,因此没有必要中断它

  • 如果在具有线程池的执行器中运行
    run()
    方法,则很可能根本不想终止线程或关闭执行器。。。在那一点上。如果确实要关闭执行器,则应调用其关闭方法之一


例如,这里有一个版本,没有所有的中断内容,也没有线程创建/破坏搅动,你看起来在做什么

public class TaskExecutor {

    private ExecutorService executor = new ThreadPoolExecutorService(...);

    public void submitTask1(final SomeTask task) {
        executor.submit(new Runnable(){
            public void run() {
                doTask(task);
                submitTask2(task);
            }
        });
    }

    public void submitTask2(final SomeTask task) {
        executor.submit(new Runnable(){
            public void run() {
                doTask2(task);
            }
        });
    }

    public void shutdown() {
        executor.shutdown();
    }
 }

如果要为任务单独排队,只需创建并使用两个不同的执行器即可

听起来好像处理第一个队列的线程知道一旦队列耗尽,就不会有更多的任务出现。这听起来很可疑,但我会相信你的话,并提出一个解决方案

定义一个对两个线程都可见的
AtomicInteger
。将其初始化为正1

定义第一个线程的操作,如下所示:

  • 队列上循环#poll()
  • 如果
    Queue#poll()
    返回null,则对共享整数调用
    AtomicInteger#decrementAndGet()
    • 如果
      AtomicInteger#decrementAndGet()
      返回零,则通过
      thread#interrupt()
      中断第二个线程。(这将处理没有项目到达的情况。)
    • 在这两种情况下,退出循环
  • 否则,处理提取的项,对共享整数调用
    AtomicInteger#incrementAndGet()
    ,将提取的项添加到第二个线程的队列中,然后继续循环
定义第二个线程的操作,如下所示:

  • 循环阻塞
    阻塞队列#take()
  • 如果
    BlockingQueue#take()
    抛出
    InterruptedException
    ,捕获异常,调用
    Thread.currentThread().interrupt()
    ,然后退出循环
  • 否则,处理提取的项目
  • 对共享整数调用
    AtomicInteger#decrementAndGet()
    • 如果
      AtomicInteger#decrementAndGet()
      返回零,则退出循环
    • 否则,继续循环
在尝试编写实际代码之前,请确保您理解了这个想法。约定是第二个线程继续等待队列中的更多项目,直到预期任务数达到零。此时,生产线程(第一个线程)将不再将任何新项目推送到第二个线程的队列中,因此第二个线程知道停止服务其队列是安全的

当没有任务到达第一个线程队列时,就会出现这种情况。因为第二个线程在处理一个项目之后只递减和测试计数,如果它从来没有机会处理任何项目,它就不会考虑停止。我们使用线程中断来处理这种情况,代价是在第一个线程的循环终止步骤中使用另一个条件分支。幸运的是,该分支只执行一次


有许多设计可以在这里工作。我只描述了一个只引入了一个额外实体的例子,即共享原子整数,但即使如此,它也很复杂。我认为使用毒丸会更干净,尽管我承认无论是
Queue#add()
还是
BlockingQueue#put()
都不接受null作为有效元素(由于
Queue#poll()
的返回值契约)。否则,很容易将null用作毒药。

听起来好像处理第一个队列的线程知道一旦其队列耗尽,就不会有更多任务出现。这听起来很可疑,但我会相信你的话,并提出一个解决方案

定义一个对两个线程都可见的
AtomicInteger
。初始化