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

时间:2015-12-14 08:20:44

标签: java multithreading concurrency synchronization blocking

我应该使用哪个Java同步对象来确保完成任意大量的任务?限制是:

  1. 每项任务都需要花费很多时间才能完成,并且可以并行执行任务。
  2. 有太多的任务要适应内存(即我不能将Future的每个任务放入Collection,然后在所有期货上调用get
  3. 我不知道会有多少任务(即我不能使用CountDownLatch)。
  4. ExecutorService可能会被共享,因此我无法使用awaitTermination( long, TimeUnit )
  5. 例如,在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个项目):Processed 128 items in 1.846794962883 seconds.

    我尝试了与Phaser类似的东西:

    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." );
    

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

3 个答案:

答案 0 :(得分:1)

此示例中Phaser用法的问题是CallerRunsPolicy允许任务在启动线程上执行。因此,当循环仍在进行中时,到达方的数量可以等于登记方的数量,从而导致阶段增加。解决方案是使用1方初始化Phaser,然后在循环结束时,到达并等待其他方到达。这可确保在所有任务完成之前阶段不会增加到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." );

答案 1 :(得分:1)

&#34;确保完成任意大量的任务&#34; - 最简单的方法是维护已完成任务的计数器,使用阻塞操作等待达到给定数量的任务。没有这样的现成课程,但很容易做出一个:

class EventCounter {
   long counter=0;

   synchronized void up () {
     counter++;
     notifyAll();
   }
   synchronized void ensure (long count) {
     while (counter<count) wait();
   }
 }

&#34;有太多的任务要适应内存&#34; - 因此,当运行任务的数量过多时,必须暂停提交新任务的过程。最简单的方法是将运行任务的数量视为资源,并使用信号量计算:

Semaphore runningTasksSema=new Semaphore(maxNumberOfRunningTasks);
EventCounter  eventCounter =new EventCounter ();

for( final String item : fetchItems() ) {
    final Runnable task = new Runnable() {
       public void run() {
            processItem( item ); 
            runningTasksSema.release();
            eventCounter.up();
       }
    };
   runningTasksSema.aquire();
   executor.execute(task);
}

当线程想要确保完成一定数量的任务时,它会调用:

eventCounter.ensure(givenNumberOfFinishedTasks);

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

答案 2 :(得分:0)

如果您使用的是java8,则可以使用CompletableFuture

java.util.concurrent.CompletableFuture.allOf(CompletableFuture<?>... cfs)

将等待传递数组中所有期货的结果。