Java—使用公共executor服务实例进行并发处理

Java—使用公共executor服务实例进行并发处理,java,multithreading,concurrency,executorservice,Java,Multithreading,Concurrency,Executorservice,我有n个工作线程从一个kinesis流中检索记录(这对于这个问题并不重要),然后将这些记录推送到一个executor服务,在那里记录被处理并持久化到后端数据库。此同一执行器服务实例用于所有工作线程 现在有一个场景,任何给定的worker循环停止处理记录和块,直到它提交的所有记录都被完全处理。这本质上意味着,对于来自特定工作线程的记录,executor服务中不应存在挂起/正在运行的线程 实现的一个非常简单的示例如下: 工人阶级 public class Worker { Worker(Liste

我有n个工作线程从一个kinesis流中检索记录(这对于这个问题并不重要),然后将这些记录推送到一个executor服务,在那里记录被处理并持久化到后端数据库。此同一执行器服务实例用于所有工作线程

现在有一个场景,任何给定的worker循环停止处理记录和块,直到它提交的所有记录都被完全处理。这本质上意味着,对于来自特定工作线程的记录,executor服务中不应存在挂起/正在运行的线程

实现的一个非常简单的示例如下:

  • 工人阶级

    public class Worker {
    
    Worker(Listener listener){
        this.listener = listener;
    }
    
    //called periodically to fetch records from a kinesis stream
    public void processRecords(Record records) {
    
        for (Record record : records) {
            listener.handleRecord(record);
        }
    
        //if 15 minutes has elapsed, run below code. This is blocking.
        listener.blockTillAllRecordsAreProcessed()
    }
    
    }

  • 侦听器类

    public class Listener {
    
        ExecutorService es;
    
        // same executor service is shared across all listeners.
        Listener(ExecutorService es){
            this.es = es;
        }
    
        public void handleRecord(Record record) {
            //submit record to es and return
            // non blocking
        }
    
        public boolean blockTillAllRecordsAreProcessed(){
            // this should block until all records are processed
            // no clue how to implement this with a common es
        }
    
    }
    

  • 我能想到的唯一方法是为每个工作人员提供一个本地执行器服务,并为每个批处理执行类似于
    invokeAll
    的操作,这将稍微更改实现,但完成工作。但是我觉得应该有一种更好的方法来解决这个问题。

    您可以使用CountdownLatch类来阻止,如下所示:

    public void processRecords(List<Record> records) {
     CountDownLatch latch = new CountDownLatch(records.size());
     for (Record record : records) {
         listener.handleRecord(record, latch);
     }
    
     //if 15 minutes has elapsed, run below code. This is blocking.
     listener.blockTillAllRecordsAreProcessed(latch)
     } 
    
    public class Listener {
     ExecutorService es;
     ...
     public void handleRecord(Record record, CountDownLatch latch) {
         //submit record to es and return
         // non blocking
         es.submit(()->{
            someSyncTask(record);
            latch.countDown();
            
        })
     }
    
     public boolean blockTillAllRecordsAreProcessed(CountDownLatch latch){
         System.out.println("waiting for processes to complete....");
         try {
              //current thread will get notified if all chidren's are done 
              // and thread will resume from wait() mode.
              latch.await();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
           }
    
       }
    
    
    公共作废处理记录(列表记录){
    CountDownLatch latch=新的CountDownLatch(records.size());
    用于(记录:记录){
    handleRecord(记录、闩锁);
    }
    //如果15分钟过去了,在代码下面运行。这是阻塞。
    listener.BlocktAllRecords已处理(闩锁)
    } 
    公共类侦听器{
    遗嘱执行人;
    ...
    公共无效句柄记录(记录记录、倒计时闩锁){
    //将记录提交给es并返回
    //非阻塞
    提交(()->{
    someSyncTask(记录);
    倒计时();
    })
    }
    已处理公共布尔块LLRecords(倒计时闩锁闩锁){
    System.out.println(“等待进程完成…”);
    试一试{
    //如果所有孩子都完成了,当前线程将收到通知
    //线程将从wait()模式恢复。
    satch.wait();
    }捕捉(中断异常e){
    e、 printStackTrace();
    }
    }
    }
    

    请在此处阅读更多信息:

    感谢您的回复!我看到此解决方案的一个潜在问题是,由于每15分钟只调用一次
    blockTillAllRecordsAreProcessed
    ,因此在该时间跨度内会多次调用processRecords。这将反过来向executor服务提交多个批。但是,在最终调用阻塞代码时,我们只能引用最新的闩锁对象。不过,您的回答肯定给出了一些很好的见解@是的,我也有同样的怀疑。但是,您可以使用arraylist跟踪所有闩锁,并在for循环或类似的情况下阻止所有闩锁。