Java 如果ES上的项目可以重新提交给ES,我如何知道ExecutorService何时完成
我的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之前不会成功,依此类推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
//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();
}
}