在java中运行异步作业实现正常取消的最佳方法

时间:2016-10-03 04:44:44

标签: java java.util.concurrent

假设我的应用程序中的组件有类似的界面来运行作业 -

Array
(
    [0] => Array
        (
            [number] => 5613106
        )

    [1] => Array
        (
            [number] => 56131064
        )

    [2] => Array
        (
            [number] => 56131063
        )

)

我想设置我的应用程序,以便异步运行这些作业。期望是调用cancel应该立即执行返回,结果表明它已被取消。

设置此功能的最佳方法是什么?我可以创建一个Thread对象来运行它,它有其他方法可以取消,但我也在查看我不熟悉的Future接口。

FutureTask的问题是取消不优雅,不允许我调用job.cancel()。扩展FutureTask并实现我自己的处理是不是一个好主意?

3 个答案:

答案 0 :(得分:1)

当你在任务中调用cancel时,它会向运行任务的线程发送一个中断信号。您的任务需要定期检查是否已发送该信号,并在有以下情况时作出相应的反应:

if (Thread.interrupted()) {
    performNecessaryCleanup();
    return;
}

答案 1 :(得分:1)

使用并发时,请使用语言提供的内容,而不是手动实现。

据我了解,ExecutorService应该是适合您的工具,因为您可以:

  • 为其提供将异步运行并可返回结果的作业
  • 关闭执行程序,以便取消所有正在运行的作业

实施例

public static void main(String[] args) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    List<Future<IResult>> results = new ArrayList<>();

    for (int i = 0; i < 10; i++) {
        results.add(executor.submit(new Job(i))); //start jobs
    }

    executor.shutdownNow(); //attempts to stop all running jobs

    //program flow immediatly continues
}

就像@JoeC在他的回答中解释的那样,保证所有作业被停止的条件是中断在每个作业中内部管理,因为每个线程都会被标记为<调用shutdownNow()时,em>中断。

if (Thread.interrupted()) {
    //return result cancelled
}

答案 2 :(得分:0)

调用 IJob.execute() FutureTask.run()会阻止当前线程,您需要调整 IJob FutureTask 即可。

调度 FutureTask 是最佳选择,消费者可以调用 FutureTask.get()并等待结果(即使您调用 IJob.cancel( ))。

我做了一个小小的演示,嘲笑 IJob IResult ,它使用普通线程进行调度,在生产中你应该像以前一样拥有 ExecutorService 发布示例。

如您所见,主线程可以检查调用 FutureTask.isDone()的状态,基本上您正在检查结果是否已设置。设置结果意味着 IJob 的线程已完成。

您可以随时调用 IJob.cancel()来完成 FutureTask 中包装的 IJob ,如果该方法的行为类似于您的评价。

模拟工作:

public class MockJob implements IJob {

    private boolean cancelled;

    public MockJob() {
    }

    @Override
    public IResult execute() {
        int count = 0;
        while (!cancelled) {
            try {
                count++;
                System.out.println("Mock Job Thread: count = " + count);
                if (count >= 10) {
                    break;
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                cancelled = true;
            }
        }
        return new MockResult(cancelled, count);
    }

    @Override
    public void cancel() {
        cancelled = true;
    }
}

模拟结果:

public class MockResult implements IResult {

    private boolean cancelled;
    private int result;

    public MockResult(boolean cancelled, int result) {
        this.cancelled = cancelled;
        this.result = result;
    }

    public boolean isCancelled() {
        return cancelled;
    }

    public int getResult() {
        return result;
    }
}

主要类别:

public class Main {

    public static void main(String[] args) throws InterruptedException {
        // Job
        IJob mockJob = new MockJob();

        // Async task
        FutureTask<IResult> asyncTask = new FutureTask<>(mockJob::execute);
        Thread mockJobThread = new Thread(asyncTask);

        // Show result
        Thread showResultThread = new Thread(() -> {
            try {
                IResult result = asyncTask.get();
                MockResult mockResult = (MockResult) result;
                Thread thread = Thread.currentThread();
                System.out.println(String.format("%s: isCancelled = %s, result = %d",
                        thread.getName(),
                        mockResult.isCancelled(),
                        mockResult.getResult()
                ));
            } catch (InterruptedException | ExecutionException ex) {
                // NO-OP
            }
        });

        // Check status
        Thread monitorThread = new Thread(() -> {
            try {
                while (!asyncTask.isDone()) {
                    Thread thread = Thread.currentThread();
                    System.out.println(String.format("%s: asyncTask.isDone = %s",
                            thread.getName(),
                            asyncTask.isDone()
                    ));
                    Thread.sleep(1000);
                }
            } catch (InterruptedException ex) {
                // NO-OP
            }
            Thread thread = Thread.currentThread();
            System.out.println(String.format("%s: asyncTask.isDone = %s",
                    thread.getName(),
                    asyncTask.isDone()
            ));
        });

        // Async cancel
        Thread cancelThread = new Thread(() -> {
            try {
                // Play with this Thread.sleep, set to 15000
                Thread.sleep(5000);
                if (!asyncTask.isDone()) {
                    Thread thread = Thread.currentThread();
                    System.out.println(String.format("%s: job.cancel()",
                            thread.getName()
                    ));
                    mockJob.cancel();
                }
            } catch (InterruptedException ex) {
                // NO-OP
            }
        });

        monitorThread.start();
        showResultThread.start();
        cancelThread.setDaemon(true);
        cancelThread.start();
        mockJobThread.start();
    }
}

输出(Thread.sleep(5000)):

Thread-2: asyncTask.isDone = false
Thread-0: count = 1
Thread-2: asyncTask.isDone = false
Thread-0: count = 2
Thread-2: asyncTask.isDone = false
Thread-0: count = 3
Thread-2: asyncTask.isDone = false
Thread-0: count = 4
Thread-2: asyncTask.isDone = false
Thread-0: count = 5
Thread-3: job.cancel()
Thread-2: asyncTask.isDone = false
Thread-1: isCancelled = true, result = 5
Thread-2: asyncTask.isDone = true

输出(Thread.sleep(15000)):

Thread-2: asyncTask.isDone = false
Thread-0: count = 1
Thread-2: asyncTask.isDone = false
Thread-0: count = 2
Thread-2: asyncTask.isDone = false
Thread-0: count = 3
Thread-2: asyncTask.isDone = false
Thread-0: count = 4
Thread-2: asyncTask.isDone = false
Thread-0: count = 5
Thread-2: asyncTask.isDone = false
Thread-0: count = 6
Thread-2: asyncTask.isDone = false
Thread-0: count = 7
Thread-2: asyncTask.isDone = false
Thread-0: count = 8
Thread-2: asyncTask.isDone = false
Thread-0: count = 9
Thread-2: asyncTask.isDone = false
Thread-0: count = 10
Thread-1: isCancelled = false, result = 10
Thread-2: asyncTask.isDone = true