Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/392.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 如果ES上的项目可以重新提交给ES,我如何知道ExecutorService何时完成_Java_Executorservice_Java.util.concurrent_Executor - Fatal编程技术网

Java 如果ES上的项目可以重新提交给ES,我如何知道ExecutorService何时完成

Java 如果ES上的项目可以重新提交给ES,我如何知道ExecutorService何时完成,java,executorservice,java.util.concurrent,executor,Java,Executorservice,Java.util.concurrent,Executor,我的Java应用程序处理文件夹中的音乐文件,它被设计为并行和独立地处理多个文件夹。为此,每个文件夹由ExecutorService处理,该服务的最大池大小与计算机的CPU数量匹配 例如,如果我们有8个CPU的计算机,那么8个文件夹(理论上)可以并发处理,如果我们有16个CPU的计算机,那么16个文件夹可以并发处理。如果我们只有1个CPU,那么我们将池大小设置为3,以允许CPU在一个文件夹被I/O阻塞时继续执行某些操作 然而,我们实际上并不是只有一个ExecutorService,而是有多个Exe

我的Java应用程序处理文件夹中的音乐文件,它被设计为并行和独立地处理多个文件夹。为此,每个文件夹由ExecutorService处理,该服务的最大池大小与计算机的CPU数量匹配

例如,如果我们有8个CPU的计算机,那么8个文件夹(理论上)可以并发处理,如果我们有16个CPU的计算机,那么16个文件夹可以并发处理。如果我们只有1个CPU,那么我们将池大小设置为3,以允许CPU在一个文件夹被I/O阻塞时继续执行某些操作

然而,我们实际上并不是只有一个ExecutorService,而是有多个ExecutorService,因为每个文件夹都可以经历多个阶段

Process1(使用ExecutorService1)→ 进程2(执行器服务2)→ 进程3(执行器服务3)

进程1、2、3等所有实现都是可调用的,并且都有自己的关联ExecutorService。我们启动了一个文件加载程序,加载文件夹,然后为每个文件夹创建一个可调用的Process1,并提交给Process1 executor,对于每个可调用的Process1,它将执行其工作,然后提交给不同的可调用程序,这可能是Process2,Process3 ecetera,但我们从不后退,例如,Process3永远不会提交给Process1。 实际上,我们有12个进程,但任何特定的文件夹都不可能经历所有12个进程

但我意识到这是有缺陷的,因为在一台16 CPU的计算机中,每个ES的池大小可以是16,所以我们实际上有48个线程在运行,这将导致太多的争用

所以我要做的是让所有进程(Process1,Process2…)使用相同的ExecutorService,这样我们就只需要与CPU匹配的工作线程

但是,在我目前的情况下,我们有一个SongLoader进程,它只提交了一个任务(加载所有文件夹),然后我们调用shutdown(),直到所有内容都提交到Process0,这个过程才会完成,然后Process0上的shutdown()在所有内容都发送到Process1之前不会成功,依此类推

 //Init Services
 services.add(songLoaderService);
 services.add(Process1.getExecutorService());
 services.add(Process2.getExecutorService());
 services.add(Process3.getExecutorService());

 for (ExecutorService service : services)
     //Request Shutdown
     service.shutdown();

     //Now wait for all submitted tasks to complete
     service.awaitTermination(10, TimeUnit.DAYS);
 }
 //...............
 //Finish Off work
但是,如果所有内容都在同一个ES上,并且Process1正在提交给Process2,那么这将不再起作用,因为在调用shutdown()时,并非Process1将提交给Process2的所有文件夹,因此它将提前关闭

那么,当该ES上的任务可以提交给同一ES上的其他任务时,如何使用单个ExecutorService检测所有工作何时已完成?

还是有更好的方法

注意,您可能会想,他为什么不将流程1、2和3的逻辑合并到一个流程中呢。困难在于,虽然我最初是按文件夹对歌曲进行分组的,但有时歌曲会被分成更小的组,它们会被分配到一行中的不同进程,而不一定是同一个进程,实际上总共有12个进程

基于Sholms理念的尝试

主线程

    private static List<Future> futures = Collections.synchronizedList(new ArrayList<Future>());
    private static AnalyserService analyserService = new MainAnalyserService(SongKongThreadGroup.THREAD_WORKER);
    ...
    SongLoader loader = SongLoader.getInstanceOf(parentFolder);
    ExecutorService songLoaderService =  SongLoader.getExecutorService();
    songLoaderService.submit(loader);
    for(Future future : futures)
    {
        try
        {
             future.get();
        }
        catch (InterruptedException ie)
        {
            SongKong.logger.warning(">>>>>> Interrupted - shutting down tasks immediately");
            getAnalyserService().getExecutorService().awaitTermination(30, TimeUnit.SECONDS);
        }
        catch(ExecutionException e)
        {
            SongKong.logger.log(Level.SEVERE, ">>>>>> ExecutionException:"+e.getMessage(), e);
        }
    }
    songLoaderService.shutdown();
现在我重新设置我不能让一个线程调用future.get()(等待完成),而同时其他线程正在添加到列表中。

不要
关机()
执行器服务。相反,创建
可调用的
对象,并保留它们创建的
未来
对象。 现在,您可以等待
Future
对象,而不是等待
ExecutorService
。请注意,现在您必须分别等待未来的每个对象,但是如果您只需要知道最后一个对象何时完成,那么您也可以按任意给定的顺序对它们进行迭代,并调用
get()

任何任务都可以提交更多任务,并且需要确保将其未来对象放入一个队列中,该队列将由主线程监控

// put these somewhere public
ConcurrentLinkedQueue<Future<Boolean>> futures = new ConcurrentLinkedQueue<Future<Boolean>>();
ExecutorService executor = ...

void submit(Callable<Boolean> c) {
    futures.add(executor.submit(c));
}
//把这些放在公共场所
ConcurrentLinkedQueue futures=新的ConcurrentLinkedQueue();
executor服务executor=。。。
无效提交(可调用c){
期货。添加(执行人。提交(c));
}
现在,主线程可以开始提交任务并等待所有任务和子任务:

void mainThread() {
    // add some tasks from main thread
   for(int i=0 ; i<N ; ++i){
        Callable<Boolean> callable = new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                ...
            }
        submit(callable);
    }

    Future<Boolean> head = null;
    while((head=futures.poll()) != null){
        try {
            head.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    // At this point, all of your tasks are complete including subtasks.
    executor.shutdown();
    executor.awaitTermination(); // should return almost immediately
}
void主线程(){
//从主线程添加一些任务

对于(int i=0;i我同意Shloim的观点,这里不需要多个
ExecutorService
实例——只需要一个(大小与可用的CPU数量相同)实际上,我认为您可能不需要
ExecutorService
;如果您使用外部机制来传递完整性,一个简单的
Executor
就可以完成这项工作

首先,我将构建一个类来表示一个较大工作项的整体。如果您需要使用每个子工作项的结果,可以使用队列,但是如果您只想知道是否还有工作要做,则只需要一个计数器

例如,您可以这样做:

public class FolderWork implements Runnable {
    private final Executor executor;
    private final File folder;

    private int pendingItems;  // guarded by monitor lock on this instance

    public FolderWork(Executor executor, File folder) {
        this.executor = executor;
        this.folder = folder;
    }

    @Override
    public void run() {
        for (File file : folder.listFiles()) {
            enqueueMoreWork(file);
        }
    }

    public synchronized void enqueueMoreWork(File file) {
        pendingItems++;
        executor.execute(new FileWork(file, this));
    }

    public synchronized void markWorkItemCompleted() {
        pendingItems--;
        notifyAll();
    }

    public synchronized boolean hasPendingWork() {
        return pendingItems > 0;
    }

    public synchronized void awaitCompletion() {
       while (pendingItems > 0) {
           wait();
       }
    }
}

public class FileWork implements Runnable {
    private final File file;
    private final FolderWork parent;

    public FileWork(File file, FolderWork parent) {
        this.file = file;
        this.parent = parent;
    }

    @Override
    public void run() {
        try {
           // do some work with the file

           if (/* found more work to do */) {
               parent.enqueueMoreWork(...);
           }
        } finally {
            parent.markWorkItemCompleted();
        }
    }
}
如果您担心
pendingItems
计数器的同步开销,您可以使用
AtomicInteger
来代替它。然后您需要一个单独的机制来通知等待的线程我们已经完成了操作;例如,您可以使用
CountDownLatch
。下面是一个示例实现:

public class FolderWork implements Runnable {
    private final Executor executor;
    private final File folder;

    private final AtomicInteger pendingItems = new AtomicInteger(0);
    private final CountDownLatch latch = new CountDownLatch(1);

    public FolderWork(Executor executor, File folder) {
        this.executor = executor;
        this.folder = folder;
    }

    @Override
    public void run() {
        for (File file : folder.listFiles()) {
            enqueueMoreWork(file);
        }
    }

    public void enqueueMoreWork(File file) {
        if (latch.getCount() == 0) {
            throw new IllegalStateException(
                "Cannot call enqueueMoreWork() again after awaitCompletion() returns!");
        }
        pendingItems.incrementAndGet();
        executor.execute(new FileWork(file, this));
    }

    public void markWorkItemCompleted() {
        int remainingItems = pendingItems.decrementAndGet();
        if (remainingItems == 0) {
            latch.countDown();
        }
    }

    public boolean hasPendingWork() {
        return pendingItems.get() > 0;
    }

    public void awaitCompletion() {
       latch.await();
    }
}
你可以这样称呼它:

Executor executor = Executors.newCachedThreadPool(...);
FolderWork topLevel = new FolderWork(executor, new File(...));
executor.execute(topLevel);
topLevel.awaitCompletion();

此示例仅显示一个级别的子工作项,但您可以使用任意数量的子工作项,只要它们都使用相同的
pendingItems
计数器来跟踪还有多少工作要做。

这是Essentially@DanielPrydens解决方案,但我对其进行了一些调整,以便更清楚地显示如何解决我的问题关节问题

创建了一个新类MainAnalyzerService,该类处理ExecutorService的创建,并提供在提交新的可调用任务和任务完成时计数的功能

public class MainAnalyserService 
{
    public static final int MIN_NUMBER_OF_WORKER_THREADS = 3;
    protected static int BOUNDED_QUEUE_SIZE = 100;

    private final AtomicInteger pendingItems = new AtomicInteger(0);
    private final CountDownLatch latch = new CountDownLatch(1);

    private static final int TIMEOUT_PER_TASK = 30;

    protected  ExecutorService      executorService;

    protected String threadGroup;

    public MainAnalyserService(String threadGroup)
    {
       this.threadGroup=threadGroup;
       initExecutorService();
    }

    protected void initExecutorService()
    {
        int workerSize = Runtime.getRuntime().availableProcessors();
        //Even if only have single cpu we still have multithread so we dont just have single thread waiting on I/O
        if(workerSize< MIN_NUMBER_OF_WORKER_THREADS)
        {
            workerSize = MIN_NUMBER_OF_WORKER_THREADS;
        }

        executorService = new TimeoutThreadPoolExecutor(workerSize,
                new SongKongThreadFactory(threadGroup),
                new LinkedBlockingQueue<Runnable>(BOUNDED_QUEUE_SIZE),
                TIMEOUT_PER_TASK,
                TimeUnit.MINUTES);
    }

    public void submit(Callable<Boolean> task) //throws Exception
    {
        executorService.submit(task);
        pendingItems.incrementAndGet();
    }

    public void workDone()
    {
        int remainingItems = pendingItems.decrementAndGet();
        if (remainingItems == 0)
        {
            latch.countDown();
        }
    }

    public void awaitCompletion() throws InterruptedException{
        latch.await();
    }
}
然后,任何可调用(如Process1、Process2等)调用submit(),在ExecutorServic上提交一个新的可调用
Executor executor = Executors.newCachedThreadPool(...);
FolderWork topLevel = new FolderWork(executor, new File(...));
executor.execute(topLevel);
topLevel.awaitCompletion();
public class MainAnalyserService 
{
    public static final int MIN_NUMBER_OF_WORKER_THREADS = 3;
    protected static int BOUNDED_QUEUE_SIZE = 100;

    private final AtomicInteger pendingItems = new AtomicInteger(0);
    private final CountDownLatch latch = new CountDownLatch(1);

    private static final int TIMEOUT_PER_TASK = 30;

    protected  ExecutorService      executorService;

    protected String threadGroup;

    public MainAnalyserService(String threadGroup)
    {
       this.threadGroup=threadGroup;
       initExecutorService();
    }

    protected void initExecutorService()
    {
        int workerSize = Runtime.getRuntime().availableProcessors();
        //Even if only have single cpu we still have multithread so we dont just have single thread waiting on I/O
        if(workerSize< MIN_NUMBER_OF_WORKER_THREADS)
        {
            workerSize = MIN_NUMBER_OF_WORKER_THREADS;
        }

        executorService = new TimeoutThreadPoolExecutor(workerSize,
                new SongKongThreadFactory(threadGroup),
                new LinkedBlockingQueue<Runnable>(BOUNDED_QUEUE_SIZE),
                TIMEOUT_PER_TASK,
                TimeUnit.MINUTES);
    }

    public void submit(Callable<Boolean> task) //throws Exception
    {
        executorService.submit(task);
        pendingItems.incrementAndGet();
    }

    public void workDone()
    {
        int remainingItems = pendingItems.decrementAndGet();
        if (remainingItems == 0)
        {
            latch.countDown();
        }
    }

    public void awaitCompletion() throws InterruptedException{
        latch.await();
    }
}
analyserService = new MainAnalyserService(THREAD_WORKER);

//SongLoader uses CompletionService when calls LoadFolderWorkers so shutdown wont return until all initial folder submissions completed
ExecutorService songLoaderService = SongLoader.getExecutorService();
songLoaderService.submit(loader);
songLoaderService.shutdown();

//Wait for all aysnc tasks to complete
analyserService.awaitCompletion();
public Boolean call() 
{
    try
    {
        //do stuff
        //Possibly make multiple calls to                      
        FixSongsController.getAnalyserService().submit();
    }
    finally
    {
        FixSongsController.getAnalyserService().workDone();
    }
}