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