Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/371.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 工作人员既是生产者又是消费者的线程池_Java_Multithreading_Concurrency_Producer Consumer_Java.util.concurrent - Fatal编程技术网

Java 工作人员既是生产者又是消费者的线程池

Java 工作人员既是生产者又是消费者的线程池,java,multithreading,concurrency,producer-consumer,java.util.concurrent,Java,Multithreading,Concurrency,Producer Consumer,Java.util.concurrent,我有一个无限的作业队列,可以异步处理。每个作业的处理可能会也可能不会触发为此队列创建新作业 我希望有一个由多个工作线程组成的池,从该队列中获取项目并并行处理它们,直到队列都为空和所有工作线程都处于空闲状态,等待队列上的新作业(因为忙碌的工作线程最终可能会向队列中添加新作业) 是否有一种使用java.util.concurrent实现的方法,我可以用它来解决工人也是生产者的这个特殊问题?目前尚不清楚API是否直接支持这种场景 特别是,我希望能够检测终止条件,即,当没有更多的作业可用时(空作业队列)

我有一个无限的作业队列,可以异步处理。每个作业的处理可能会也可能不会触发为此队列创建新作业

我希望有一个由多个工作线程组成的池,从该队列中获取项目并并行处理它们,直到队列都为空所有工作线程都处于空闲状态,等待队列上的新作业(因为忙碌的工作线程最终可能会向队列中添加新作业)

是否有一种使用
java.util.concurrent
实现的方法,我可以用它来解决工人也是生产者的这个特殊问题?目前尚不清楚API是否直接支持这种场景

特别是,我希望能够检测终止条件,即,当没有更多的作业可用时(空作业队列),将不会产生更多的作业(所有空闲工作线程)

编辑

Nam San下面的答案似乎是最优雅的方法,它基本上归结为跟踪提交作业的数量与完成作业的数量,并使用这些数量相等的情况作为终止条件

我已经使用
java.util.concurrent
实现实现了一个完整的示例,它扩展了
ThreadPoolExecutor
来实现这一点,并专门处理作业队列,以接受以特定方式排序的
可比较的
实例

  • :一个自定义执行器,它扩展了
    ThreadPoolExecutor
    ,但有额外的方法来执行可能创建新作业的作业,还有一个新的等待方法,等待所有提交的作业完成
  • :一个可比较的可运行作业的示例,该作业可能会创建新作业以提交给
    测试执行器
  • :包含使用带有
    测试执行器的
    工作单元
    实例运行示例的主要方法

我认为消费者也是生产者并不重要,因为在生产者-消费者模式中,他们是完全独立的关注点

您的消费者已经有了对队列的引用-只需让他们像生产者一样添加到队列中即可


您可以使用
AtomicInteger
或类似工具来记录当前活动的工作人员数量,如果您想等待所有工作人员都静止下来,也可以使用
CountDownLatch

我已经看到了一些解决此类问题的不同方法

一种方法是在主线程中仍然使用
poll
作为阻塞调用,就像在代码中一样,但将工作线程中的“虚拟”对象排队,以唤醒主线程,否则它可能会永远等待。例如,任何未向队列添加更多项而完成的工作线程都应提交一个虚拟作业,主线程会识别并忽略该作业(它只用于唤醒主线程)。通过跟踪活动作业的数量,您可以创建更少的虚拟对象,从而减少“虚假唤醒”,但代价是增加一些复杂性—只有最后一个作业需要添加虚拟对象

另一种方法是等待另一个对象。例如,任何旧的
对象
都可以。在此对象上设置主线程
wait()
。然后作业在完成时使用
Object.notify()
唤醒此线程。同样,通过计数,您可以减少所需通知的数量

最优雅的解决方案可能是使用
信号灯
。基本上,信号量的值将是“飞行中作业+队列项目”数量的负数。当作业从队列中拾取一个项目时,此值不会更改(因为正在运行的作业上升一个,而队列项目下降一个),但每个作业都应该为其添加的每个作业调用reducePermits(),并在完成之前调用一个release()

然后主线程可以在工作期间阻塞
acquire()
。当它醒来时,一切都完成了(因为在飞行中+排队工作为零)。您需要启动另一个线程来实际执行
poll
调用并添加作业(当前由主线程完成),当主线程上的
acquire
返回时,此工作线程可以关闭。但是,让现有的worker
poll()
自己完成可能更简单。那么你根本不需要这个传递函数


事实上,使用
信号量解决方案
时,为什么不完全取消队列,使用内置在执行器中的队列呢?也就是说,让工人通过
executor.submit(newJob(nextJob))
提交新工作?在内部,执行器线程无论如何都在从阻塞队列中提取工作,因此在拥有显式外部队列时存在一些重复。

年前,我不得不做一些类似的事情,但使用有界堆栈。我将分享一个可能的解决方案:

idle_thread = MAX_THREAD;
do
{
    if(queue != empty) // If thread have work to do
    {
       idle_threads--;  // Count this threads was a worker   
       flag = true;
       while(queue != empty)  // Until queue have work
       {
          synchronized(this)
          {
            // task =  take_out_of_queue;
          }
        }
   }
   if(flag) // This flag must to be local to each thread, it is use to insure 
   {        // that threads will count this only one time for each time 
          // the queue got empty
         synchronized(this)
         {
            if(flag == false)
            idle_threads++;  // Count thread as a idle one
            flag = false;
         }
     }
     if(idle_threads == MAX_THREADS) out = true; // When all threads are idle stop the work loop
} while(!out)

请看我在上面的帖子,它满足了大多数要求。但它并没有在期货和可赎回债券上实现。我得想清楚。每项任务都没有得到重视。没有产生结果和异常。这只是一种并行和递归的文件扫描方式。

下面的代码演示了如何使用
执行器周围的包装器类来计算提交的作业数,并将其与完成的作业数进行比较,以实现所需目标。请注意,您的任务必须调用包装类的
execute
方法,并且决不能直接调用底层的
executer
。如果需要,扩展下面的包装来包装
ExecutorService
的“submit”方法应该很简单

public class ExampleExecutor {

    private final Executor executor;
    private long submitCount = 0;
    private long doneCount = 0;

    public ExampleExecutor(Executor executor) {
        this.executor = executor;
    }

    public synchronized void execute(Collection<Runnable> commands) {
        for (Runnable command : commands) {
            execute(command);
        }
    }

    public synchronized void execute(final Runnable command) {
        submitCount ++;

        executor.execute(new Runnable() {
            public void run() {
                try {
                    command.run();
                } finally {
                    synchronized (ExampleExecutor.this) {
                        doneCount++;
                        if (doneCount == submitCount) {
                            ExampleExecutor.this.notifyAll();
                        }
                    }
                }
            }
        });
    }

    public synchronized void awaitCompletion() throws InterruptedException {
        while (doneCount != submitCount) {
            this.wait();
        }
    }
}
公共类示例执行器{
私人最终执行人;
私有长submitCount=0;
私有长doneCount=0;
公共示例执行器(执行器执行器){
this.executor=执行人;
}
公共同步作废执行(收集)
public class Test {

    static class Task implements Runnable {
        private final String id;
        private final long repetitions;
        private final long respawnSize;
        private final ExampleExecutor executor;

        public Task(String id, long repetitions, long respawnSize, ExampleExecutor executor) {
            this.id = id;
            this.repetitions = repetitions;
            this.respawnSize = respawnSize;
            this.executor = executor;
        }

        public void run() {
            for (int i = 0; i < respawnSize; i ++) {
                // Spawning new sub tasks
                executor.execute(new Task(id + "-" + i, repetitions/2, 0, null));
            }

            double sum = 0;
            for (int i = 0; i < repetitions; i++) {
                sum += Math.sin(i);
            }

            System.err.println(id + " completed at " + System.currentTimeMillis() + " with sum=" + sum);
        }
    }

    public static void main(String argv[]) throws InterruptedException {
        ExampleExecutor executor = new ExampleExecutor(Executors.newFixedThreadPool(2));
        executor.execute(new Task("0", 2000000, 100, executor));

        System.err.println("main thread awaits completion");
        executor.awaitCompletion();
        System.err.println("main thread recieved completion event");
    }
}