这实际上是一个半问题,半讨论的话题。
我认为Java需要一个功能,即可以通过类(即ParallelExecuter)中的注释定义方法(可以说“ calcTotalX”),该类将在同一类中的另一个方法(可以说doJob)的Start / AfterEnd之前执行。因此,我们可以确保任何扩展ParallelExecuter并覆盖其“ doJob”方法的类(让我们说SortingParallelExecuter)都不必了解X,不必冒着处理X的风险,无需处理有关X的操作事件,等等。
我的问题是,除了AOP之外,Java中有什么可以做到的呢?
我之所以不选择AOP,是因为它会使代码变得如此分散且难以阅读。
同样,这里的关注点是类/方法/属性特定的。因此,其他类/方法/属性不需要复制行为。
顺便说一句,如果您认为合理,请对该主题投票。 n
好的,为具体起见,我添加了一个示例类,用于划分和并行化。
public abstract class PartitionedParallelExecutor<T> {
private ExecutorService executorService;
private final List<PartitionErrorDesc<T>> errorMap = new ArrayList<PartitionErrorDesc<T>>();
private final AtomicInteger totalExecutedJobCount = new AtomicInteger();
private boolean shutdownForced = false;
private final int workerCount;
private final int partitionCount;
protected final List<T> sourceList;
//Must be implemented via Extender class
protected abstract PartitionErrorDesc<T> doWork(List<T> subList);
public PartitionedParallelExecutor(int workerCount, int partitionCount, List<T> sourceList) {
super();
this.workerCount = workerCount;
this.partitionCount = partitionCount;
this.sourceList = sourceList;
}
public Object onPerPartitionFail(List<T> subList, PartitionErrorDesc<T> ped){return null;};
public Object onPerPartitionSuccess(List<T> subList){return null;};
public Object onAnyFailDoOnce() {return null;}
public Object onTotalSuccess() {return null;}
public final void fireAndWait() {
if(workerCount <= 0 || partitionCount <= 0 ||
sourceList == null || sourceList.size() == 0){
throw new IllegalArgumentException();
}
ExecutorService executorService = Executors.newFixedThreadPool(workerCount);
this.executorService = executorService;
List<List<T>> partitions = partitionList(sourceList, partitionCount);
for (final List<T> subList : partitions) {
executorService.execute(new Runnable() {
@Override
public void run() {
PartitionErrorDesc<T> errorDesc = null;
try {
errorDesc = doWork(subList);
} catch (Throwable e) {
errorDesc = new PartitionErrorDesc<T>(subList);
errorDesc.setSuccess(false);
errorDesc.setE(e);
errorDesc.setFailedAtItem(0);
}
errorMap.add(errorDesc);
if(errorDesc.isSuccess == false) { //failure
onPerPartitionFail(subList, errorDesc);
setShutdownForced(true);
totalExecutedJobCount.addAndGet(errorDesc.getFailedAtItem());
Thread.currentThread().interrupt();
return;
} else { //success
totalExecutedJobCount.addAndGet(subList.size());
onPerPartitionSuccess(subList);
}
}
});
}
executorService.shutdown();
try {
executorService.awaitTermination(60, TimeUnit.MINUTES);
} catch (InterruptedException e) {
setShutdownForced(true);
Thread.currentThread().interrupt();
}
if (!isShutdownForced()) {
onTotalSuccess();
} else {
onAnyFailDoOnce();
}
}
private List<List<T>> partitionList(List<T> sourceList , int partitionCount) {
List<List<T>> partitions = new ArrayList<List<T>>();
int totalSize = sourceList.size();
int pageCount = partitionCount;
int pageSize = totalSize / pageCount;
int remainder = totalSize % (pageSize * pageCount);
int fromIndex = 0;
int toIndex = 0;
for(int i = 0; i < pageCount; i++) {
fromIndex = toIndex;
if(toIndex >= totalSize){
break;
}
if ( remainder > i) {
toIndex = toIndex + pageSize + 1;
} else {
toIndex = toIndex + pageSize;
}
List<T> subList = sourceList.subList(fromIndex,toIndex);
partitions.add(subList);
}
return partitions;
}
public final void shutdownNow() {
setShutdownForced(true);
List<Runnable> runnables = executorService.shutdownNow();
try {
if(!executorService.awaitTermination(60,TimeUnit.SECONDS)) {
LOG.error("pool didnt terminate after 60 seconds in shutdownNow");
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
public final boolean isShutdownForced() {
return shutdownForced;
}
private synchronized void setShutdownForced(boolean shutdownForced) {
this.shutdownForced = shutdownForced;
}
}
在此示例中,想要使用上述类以多线程方式完成其工作的程序员,他必须实现“ doJob()”,但调用“ fireAndWait()”。实现doJob并调用doJob将是一种更简洁的方法。诸如计算“ totalExecutedJobCount”,onPerPartitionFail()之类的其余事情必须以交叉于“ doJob”方法的AOP方式实现。是的,我们可以在另一个类中实现此功能,但是据我所知,任何扩展PartitionedParallelExecutor的类也可以扩展此AOP行为。在这一点上,我问为什么这些东西(计算“ totalExecutedJobCount”,onPerPartitionFail())必须在另一个类中。它们与该类,其属性和方法有关。以面向对象的方式查看,它们必须在同一类中包含一些方法,并在“ doJob”结束时进行调用。就是那个问题。希望现在一切都清楚了。谢谢你的时间。
最佳答案
在讨论我们的评论之后,阅读了您对Peter的回答的评论并检查了您最近添加的示例代码,我实际上理解了您的问题,但是不明白为什么您在那里看到问题。
您的方法fireAndWait()
实际上是Template Method,这是一种成熟的OOP设计模式。我认为将doWork(List<T>)
作为算法的一部分来实现是完全可以的,但是可以指示用户(通过JavaDoc)不要自己调用它,而是依靠间接调用它。例如,您经常实现Runnable.run()
(即使在示例代码中也是如此!),但不要抱怨它不是您自己调用的,而是通过Thread.start()
或ExecutorService.execute()
间接调用的。这不是相同的模式吗?为什么一个“上帝方法”应该做所有事情?
如果您不喜欢自己的模式方法,请随意编写PartitionedParallelExecutorStatisticsAspect
(长名称表示抱歉),以解决这一方面的问题(因为这就是问题所在)。如果愿意,可以将其放入同一包中,这样它就靠近抽象类,并让它完成工作。上帝的课程和上帝的方法一样糟糕,因此可以说使用AOP也是可行的选择。做(并行分区)工作是这里的核心问题,保持统计数据是次要的。如果干净利落地使用这两种方法,我都很好。
尽管该主题在某种程度上是哲学上的,并且我在这里看到了几张近距离投票,这是可以理解的,但我仍然希望我的评论有所帮助。如果是这样,请随时接受答案并关闭主题,以免使其成为无休止的讨论话题。