Java:停止可调用线程的设计思路

时间:2011-08-26 19:40:00

标签: java multithreading

我正在编写一个执行批处理的程序。批处理元素可以彼此独立地处理,我们希望最小化整体处理时间。因此,我不是一次循环遍历批处理中的每个元素,而是使用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)语句的调用方法中包装方法调用/代码块。

有什么建议吗?

3 个答案:

答案 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将从另一个线程调用。但您可能不想使用onMessagecancelled(见下文)。

其他小问题: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被取消,它将不再被执行,因此您不必总是检查状态。