Java 同步对象,以确保完成所有任务

Java 同步对象,以确保完成所有任务,java,multithreading,concurrency,synchronization,blocking,Java,Multithreading,Concurrency,Synchronization,Blocking,我应该使用哪个Java同步对象来确保完成任意数量的任务?制约因素包括: 每项任务都需要相当长的时间才能完成,并行执行任务是合适的 内存中的任务太多(即,我无法将每个任务的未来放入集合,然后对所有未来调用get) 我不知道将有多少任务(即我不能使用倒计时闩锁) ExecutorService可能是共享的,因此我不能使用waittermination(long,TimeUnit) 例如,使用Grand Central Dispatch,我可能会执行以下操作: let workQueue = disp

我应该使用哪个Java同步对象来确保完成任意数量的任务?制约因素包括:

  • 每项任务都需要相当长的时间才能完成,并行执行任务是合适的
  • 内存中的任务太多(即,我无法将每个任务的
    未来
    放入
    集合
    ,然后对所有未来调用
    get
  • 我不知道将有多少任务(即我不能使用
    倒计时闩锁
  • ExecutorService
    可能是共享的,因此我不能使用
    waittermination(long,TimeUnit)
  • 例如,使用Grand Central Dispatch,我可能会执行以下操作:

    let workQueue = dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 )
    let latch = dispatch_group_create()
    let startTime = NSDate()
    var itemsProcessed = 0
    let countUpdateQueue = dispatch_queue_create( "countUpdateQueue", DISPATCH_QUEUE_SERIAL )
    for item in fetchItems() // generator returns too many items to store in memory
    {
        dispatch_group_enter( latch )
        dispatch_async( workQueue )
        {
            self.processItem( item ) // method takes a non-trivial amount of time to run
            dispatch_async( countUpdateQueue )
            {
                itemsProcessed++
            }
            dispatch_group_leave( latch )
        }
    }
    dispatch_group_wait( latch, DISPATCH_TIME_FOREVER )
    let endTime = NSDate()
    let totalTime = endTime.timeIntervalSinceDate( startTime )
    print( "Processed \(itemsProcessed) items in \(totalTime) seconds." )
    
    它产生如下输出(对于128项):
    在1.846794962883秒内处理了128项。

    我用
    移相器尝试了类似的方法:

    final Executor executor = new ThreadPoolExecutor( 64, 64, 1l, MINUTES, new LinkedBlockingQueue<Runnable>( 8 ), new CallerRunsPolicy() );
    final Phaser latch = new Phaser( 0 );
    final long startTime = currentTimeMillis();
    final AtomicInteger itemsProcessed = new AtomicInteger( 0 );
    for( final String item : fetchItems() ) // iterator returns too many items to store in memory
    {
        latch.register();
        final Runnable task = new Runnable() {
            public void run() {
                processItem( item ); // method takes a non-trivial amount of time to run
                itemsProcessed.incrementAndGet();
                latch.arrive();
            }
        };
        executor.execute( task );
    }
    latch.awaitAdvance( 0 );
    final long endTime = currentTimeMillis();
    out.println( "Processed " + itemsProcessed.get() + " items in " + ( endTime - startTime ) / 1000.0 + " seconds." );
    
    final Executor Executor=new ThreadPoolExecutor(64,64,1l,分钟,new LinkedBlockingQueue(8),new callerRunPolicy());
    最终相位器闩锁=新相位器(0);
    最终长起始时间=currentTimeMillis();
    final AtomicInteger itemsProcessed=新的AtomicInteger(0);
    for(最终字符串项:fetchItems())//迭代器返回的项太多,无法存储在内存中
    {
    寄存器();
    最终可运行任务=新可运行(){
    公开募捐{
    processItem(item);//方法的运行时间非常长
    itemsProcessed.incrementAndGet();
    闩锁。到达();
    }
    };
    执行者。执行(任务);
    }
    锁存提前(0);
    最终长结束时间=currentTimeMillis();
    out.println(“已处理”+项处理.get()+”项在“+(endTime-startTime)/1000.0+“秒”);
    

    任务并不总是在最后一个print语句之前完成,我可能会得到如下输出(对于128项):
    在5.296秒内处理了121项。
    相位器是否是正确的使用对象?文档表明它只支持65535个参与方,因此我需要批处理要处理的项目,或者引入某种
    Phaser
    分层。

    本例中
    Phaser
    用法的问题是
    callerRunPolicy
    允许在启动线程上执行任务。因此,当循环仍在进行时,到达方的数量可以等于注册方的数量,从而导致阶段增加。解决方案是与一方初始化
    相位器
    ,然后在环路完成时到达,并等待其他方到达。这确保在所有任务完成之前阶段不会增加到1

    final Executor executor = new ThreadPoolExecutor( 64, 64, 1l, MINUTES, new LinkedBlockingQueue<Runnable>( 8 ), new CallerRunsPolicy() );
    final Phaser latch = new Phaser( 1 );
    final long startTime = currentTimeMillis();
    final AtomicInteger itemsProcessed = new AtomicInteger( 0 );
    for( final String item : fetchItems() ) // iterator returns too many items to store in memory
    {
        latch.register();
        final Runnable task = new Runnable() {
            public void run() {
                processItem( item ); // method takes a non-trivial amount of time to run
                itemsProcessed.incrementAndGet();
                final int arrivalPhase = latch.arrive();
            }
        };
        executor.execute( task );
    }
    latch.arriveAndAwaitAdvance();
    final long endTime = currentTimeMillis();
    out.println( "Processed " + itemsProcessed.get() + " items in " + ( endTime - startTime ) / 1000.0 + " seconds." );
    
    final Executor Executor=new ThreadPoolExecutor(64,64,1l,分钟,new LinkedBlockingQueue(8),new callerRunPolicy());
    最终相位器闩锁=新相位器(1);
    最终长起始时间=currentTimeMillis();
    final AtomicInteger itemsProcessed=新的AtomicInteger(0);
    for(最终字符串项:fetchItems())//迭代器返回的项太多,无法存储在内存中
    {
    寄存器();
    最终可运行任务=新可运行(){
    公开募捐{
    processItem(item);//方法的运行时间非常长
    itemsProcessed.incrementAndGet();
    final int-arrivalPhase=lock.arrival();
    }
    };
    执行者。执行(任务);
    }
    闩锁。到达并等待前进();
    最终长结束时间=currentTimeMillis();
    out.println(“已处理”+项处理.get()+”项在“+(endTime-startTime)/1000.0+“秒”);
    
    如果您使用的是java8,则可以使用CompletableFuture

    java.util.concurrent.CompletableFuture.allOf(CompletableFuture<?>... cfs)
    
    java.util.concurrent.CompletableFuture.allOf(CompletableFuture…cfs)
    
    这将等待已传递数组中所有未来的结果。

    “确保完成任意数量的任务”-最简单的方法是维护已完成任务的计数器,使用阻塞操作等待达到给定数量的任务。没有这样的现成类,但很容易制作一个:

    class EventCounter {
       long counter=0;
    
       synchronized void up () {
         counter++;
         notifyAll();
       }
       synchronized void ensure (long count) {
         while (counter<count) wait();
       }
     }
    
    当线程希望确保完成某些给定数量的任务时,它会调用:

    eventCounter.ensure(givenNumberOfFinishedTasks);
    

    异步(非阻塞)版本的
    runningtasksema.aquire()
    eventCounter.sure()
    操作是可以设计的,但它们会更复杂。

    当你说有太多的任务无法放入内存时,这不是资源限制吗?因此,您可以并行地执行内存中的任务,并且可以使用Future。不确定是否有任何方法可以帮助您通过资源约束,最终您将得到一个非常复杂的解决方案。
    eventCounter.ensure(givenNumberOfFinishedTasks);