我正在编写一个执行批处理的程序。批处理元素可以彼此独立地处理,我们希望最小化整体处理时间。因此,我不是一次循环遍历批处理中的每个元素,而是使用ExecutorService并向其提交Callable对象:
public void process(Batch batch)
{
ExecutorService execService = Executors.newCachedThreadPool();
CopyOnWriteArrayList<Future<BatchElementStatus>> futures = new CopyOnWriteArrayList<Future<BatchElementStatus>>();
for (BatchElement element : batch.getElement())
{
Future<MtaMigrationStatus> future = execService.submit(new ElementProcessor(batch.getID(),
element));
futures.add(future);
}
boolean done = false;
while (!done)
{
for (Future<BatchElementStatus> future : futures)
{
try
{
if (future.isDone())
{
futures.remove(future);
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
if (futures.size() == 0)
{
done = true;
}
}
}
}
我们希望能够取消批处理。因为我没有使用循环,所以如果设置了取消标志,我不能只检查每个循环的顶部。
我们正在使用一个JMS主题,BatchProcessor和ElementProcessor将监听它,告知它们批处理已被取消。
ElementProcess调用()中有许多步骤,之后可以安全地停止其中一些处理,但是有一个不可返回的点。该课程有这个基本设计:
public class ElementProcessor implements Callable, MessageListener
{
private cancelled = false;
public void onMessage(Message msg)
{
// get message object
cancelled = true;
}
public BatchElementStatus call()
{
String status = SUCCESS;
if (!cancelled)
{
doSomehingOne();
}
else
{
doRollback();
status = CANCELLED;
}
if (!cancelled)
{
doSomehingTwo();
}
else
{
doRollback();
status = CANCELLED;
}
if (!cancelled)
{
doSomehingThree();
}
else
{
doRollback();
status = CANCELLED;
}
if (!cancelled)
{
doSomehingFour();
}
else
{
doRollback();
status = CANCELLED;
}
// After this point, we cannot cancel or pause the processing
doSomehingFive();
doSomehingSix();
return new BatchElementStatus("SUCCESS");
}
}
我想知道是否有更好的方法来检查批处理/元素是否已被取消,而不是在if(!cancelled)
语句的调用方法中包装方法调用/代码块。
有什么建议吗?
答案 0 :(得分:1)
我认为你不能比你现在做得更好,但这里有另一种选择:
public BatchElementStatus call() {
return callMethod(1);
}
private callMethod(int methodCounter) {
if (cancelled) {
doRollback();
return new BatchElementStatus("FAIL");
}
switch (methodCounter) {
case 1 : doSomethingOne(); break;
case 2 : doSomethingTwo(); break;
...
case 5 : doSomethingFive();
doSomethingSix();
return new BatchElementStatus("SUCCESS");
}
return callMethod(methodCounter + 1);
}
此外,您希望cancelled
为volatile,因为onMessage
将从另一个线程调用。但您可能不想使用onMessage
和cancelled
(见下文)。
其他小问题:1)CopyOnWriteArrayList<Future<BatchElementStatus>> futures
应该是ArrayList
。使用并发集合误导我们认为futures
在许多线程上。 2)while (!done)
应替换为while (!futures.isEmpty())
并删除done
。 3)您可能应该拨打future.cancel(true)
而不是“消息”取消。然后,您必须检查if (Thread.interrupted())
而不是if (cancelled)
。如果你想杀死所有未来,那么只需致电execService.shutdownNow()
;你的任务必须处理中断才能使其工作。
修改强>:
而不是while(!done) { for (... futures) { ... }}
,您应该使用ExecutorCompletionService。它做你想做的事情,它可能会做得更好。 API中有一个完整的例子。
答案 1 :(得分:0)
Future有一个cancel(boolean)方法,如果在
中传递了true,它将中断正在运行的线程所以用if(!cancelled)
替换if(Thread.interrupted())
支票并在收到中断时返回(当前不是)
请注意,这会将中断的标志重置为false(因此if(Thread.interrupted()&&Thread.interrupted())
将为false)如果您不想重置它,请使用Thread.currentThread().isInterrupted()
这为后续检查保留标志
或者您可以将标记重置为Thread.currentThread().interrupt();
除了在等待中使用这个
for(Iterator<Future<MtaMigrationStatus>> it = futures.iterator();it.hasNext();){
Future<MtaMigrationStatus> future = it.next();
try
{
if (future.isDone())
{
it.remove();//<<--this avoids concurrent modification exception in the loop
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
if (futures.size() == 0)//outside the inner for loop and inside the while (or make the condition this) for micro-optimizing this check
{
done = true;
}
答案 2 :(得分:0)
您的ElementProcessor
可以从java.util.concurrent.FutureTask
延伸到
可取消的异步计算。这个类提供了一个基础 Future的实现,有启动和取消的方法 计算,查询计算是否完成,并检索 计算的结果。
FutureTask类是Future实现的实现 Runnable,因此可以由执行者执行。
FutureTask
有一个cancel
方法,您可以通过该方法执行某些取消特定操作。此外,如果FutureTask
被取消,它将不再被执行,因此您不必总是检查状态。