/*
* @(#)ParallelShardAccessStrategy.java 2012-8-1 下午10:00:00
*
* Copyright (c) 2011-2012 Makersoft.org all rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
*
*/
package org.makersoft.shards.strategy.access.impl;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.makersoft.shards.Shard;
import org.makersoft.shards.ShardId;
import org.makersoft.shards.ShardOperation;
import org.makersoft.shards.strategy.access.ShardAccessStrategy;
import org.makersoft.shards.strategy.exit.ExitOperationsCollector;
import org.makersoft.shards.strategy.exit.ExitStrategy;
import org.makersoft.shards.utils.Assert;
import org.makersoft.shards.utils.Lists;
/**
* 并行访问策略
*/
public class ParallelShardAccessStrategy implements ShardAccessStrategy{
private final Log log = LogFactory.getLog(getClass());
private final ThreadPoolExecutor executor;
public ParallelShardAccessStrategy(ThreadPoolExecutor executor) {
Assert.notNull(executor);
this.executor = executor;
}
@Override
public <T> T apply(List<Shard> shards, ShardOperation<T> operation,
ExitStrategy<T> exitStrategy,
ExitOperationsCollector exitOperationsCollector) {
List<StartAwareFutureTask> tasks = Lists.newArrayListWithCapacity(shards.size());
int taskId = 0;
/**
* Used to prevent threads for processing until all tasks have been
* submitted, otherwise we risk tasks that want to cancel other tasks
* that have not yet been scheduled.
*/
CountDownLatch startSignal = new CountDownLatch(1);
/**
* Used to signal this thread that all processing is complete
*/
CountDownLatch doneSignal = new CountDownLatch(shards.size());
for(final Shard shard : shards) {
// create a task for each shard
for(final ShardId shardId : shard.getShardIds()) {
ParallelShardOperationCallable<T> callable =
new ParallelShardOperationCallable<T>(
startSignal,
doneSignal,
exitStrategy,
operation,
shard,
shardId,
tasks);
// wrap the task in a StartAwareFutureTask so that the task can be cancelled
StartAwareFutureTask ft = new StartAwareFutureTask(callable, taskId++);
tasks.add(ft);
// hand the task off to the executor for execution
executor.execute(ft);
}
}
// the tasks List is populated, release the threads!
startSignal.countDown();
try {
log.debug("Waiting for threads to complete processing before proceeding.");
//TODO(maxr) let users customize timeout behavior
/*
if(!doneSignal.await(10, TimeUnit.SECONDS)) {
final String msg = "Parallel operations timed out.";
log.error(msg);
throw new HibernateException(msg);
}
*/
// now we wait until all threads finish
doneSignal.await();
} catch (InterruptedException e) {
// not sure why this would happen or what we should do if it does
log.error("Received unexpected exception while waiting for done signal.", e);
}
log.debug("Compiling results.");
return exitStrategy.compileResults(exitOperationsCollector);
}
}