无法中断ExecutorService的任务

时间:2019-01-28 22:28:26

标签: java android executorservice interrupt-handling

编辑:

为了在Android环境之外测试此问题,我创建了一个Java应用程序,该应用程序创建一个ExecutorService,提供一个AttackScript(相同类)的任务,然后终止。

正常工作100%,线程被中断,任务停止。

您甚至不必通过任务Future.cancel(true)取消任务。 ExecutorService.shutdownNow()完成了工作。 Android的Service中是否有某种东西与线程池混在一起?

可正常使用的代码:

public static void main(String[] args) {
        AttackScript script = new AttackScript("http://ninjaflex.com/");

        ExecutorService executor = Executors.newFixedThreadPool(5);
        executor.submit(script);
        executor.submit(script);
        executor.submit(script);
        executor.submit(script);

        sleep(1300);

        // Automatically interrupts threads in the pool.
        executor.shutdownNow();
    }

    private static void sleep(long timeMilli){
        try {
            Thread.sleep(timeMilli);
        } catch(Exception e) {
            System.out.println("Error sleep()");
        }
    }

原始帖子:

我有一个Android Service,其中包含一个ExecutorService字段,负责执行一些任务。

任务是AttackScript类的对象。我将Future引用缓存在称为任务的Map<String,Future>中,以便以后可以取消它们。

Future future = executor.submit(new AttackScript(attack.getWebsite()));
tasks.put(attack.getPushId(), future);

Service的{​​{1}}(用户按下通知按钮时调用)中,我正在取消所有任务

onDestroy()

然后关闭执行器

private void cancelAllTasks() {
    for (Map.Entry<String, Future> futureEntry : tasks.entrySet()) {
        futureEntry.getValue().cancel(true);
    }
}

最后是 AttackScript 类:

private void shutdownThreadPool() {
     // https://www.baeldung.com/java-executor-service-tutorial
     executor.shutdown();
     try {
         if (executor.awaitTermination(800, TimeUnit.MILLISECONDS))
                executor.shutdownNow();
     } catch (InterruptedException e) {
            executor.shutdownNow();
     }
}

奇怪的是,很少有任务被中断,并且public class AttackScript implements Runnable { private static final String TAG = "AttackScript"; private URL url; public AttackScript(String website) { initializeUrl(website); } private void initializeUrl(String website) { try { url = new URL(website); } catch (MalformedURLException e) { Log.e(TAG, "Wrong url?", e); } } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { readUrl(); } Log.d(TAG, "Stopped requesting from " + url + " server."); } private void readUrl() { InputStream in = null; try { in = url.openStream(); } catch (IOException e) { Log.e(TAG, "openStream() error.", e); } finally { closeInputStream(in); } } private void closeInputStream(InputStream in) { try { in.close(); Log.d(TAG, "InputStream closed for " + url); } catch (IOException e) { Log.e(TAG, "Error while closing the input stream.", e); } } } 的执行停止,例如十分之一。但是其他9个任务没有中断,继续在AttackScript上打开openStreams()。

2 个答案:

答案 0 :(得分:4)

不得不寻找替代解决方案,我完全删除了使用线程池,现在实现了 Thread s ,存储在Map中。

再次没有发生中断,因此AtomicBoolean现在正在控制线程的执行。

private AtomicBoolean stopped = new AtomicBoolean(false);

 @Override
    public void run() {
        while (!stopped.get()) {
            readUrl();
        }
}

public void stopExecution() {
        stopped.set(true);
}

这是一个绝望的举动,但到目前为止唯一有效的举动。

答案 1 :(得分:2)

您已经通过有效的解决方法进行了回答,可以避免此问题,但是我将解释原因。问题不在于ExecutorService,而是网络库静默清除了线程的中断状态。

正如您和其他评论者所发现的,这很可能取决于您使用的特定设备及其Android版本。

从Android 4.4开始,OkHttp被用作HttpUrlConnection。在每个线程被中断与在旧版本中InputStream是否已关闭之间存在竞争状态。

作为close()调用的一部分,该代码最终被执行:

public void throwIfReached() throws IOException {
    if (Thread.interrupted()) {
        throw new InterruptedIOException("thread interrupted");
    }

    if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) {
        throw new InterruptedIOException("deadline reached");
    }
}

基于Thread.interrupted()的调用,您可以看到它清除了线程的中断状态,并且不再进行设置。

更糟糕的是,您似乎可以依靠InterruptedIOException来代替,但是在关闭流时会在内部对其进行静默处理,因此您没有机会对其进行处理。

在使用OkHttp的最新版本时,您的代码示例为我工作。在以后的版本中,似乎更加注意保持中断状态,并且它实际上按预期工作。

但是,根据一些搜索,从历史上看,中断OkHttp不能很好地发挥作用,因此,他们建议Call.cancel()来停止请求。