代码之家  ›  专栏  ›  技术社区  ›  Themelis

无法中断ExecutorService的任务

  •  8
  • Themelis  · 技术社区  · 7 年前

    编辑:

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

    这个 100%按预期工作 ,线程中断,任务停止。

    你甚至不必取消一个任务 Future.cancel(true) . ExecutorService.shutdownNow() 做这项工作。安卓有什么东西吗 Service 不知怎么搞砸了线程池?

    可作为expaccepted使用的代码:

    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()");
            }
        }
    

    原始职位:

    我有安卓系统 服务 其中包括 执行服务 字段,负责运行某些任务。

    任务是 攻击脚本 班级。我缓存 Future A中的引用 Map<String,Future> ,调用了任务,以便以后可以取消它们。

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

    在 服务 的 onDestroy() (当用户按下通知按钮时调用)我是 取消所有任务

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

    然后 关闭执行器 :

    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);
            }
        }
    }
    

    奇怪的是,很少会像10个任务中的1个那样中断任务, 攻击脚本 的执行停止。但其他9个任务没有中断,继续在上执行openstreams()。 URL S.

    2 回复  |  直到 7 年前
        1
  •  1
  •   George Mulligan    7 年前

    为了避免这个问题,你已经给出了有效的解决方法,但我会解释原因。故障不在 ExecutorService 但随着线程的中断状态被网络库自动清除。

    正如你和另一位评论者发现的,这很可能取决于你使用的特定设备及其安卓版本。

    从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() 如果可能的话。

        2
  •  4
  •   Themelis    7 年前

    被迫找到另一个解决方案我完全 远离的 A的使用 线程池 现在正在实施 单一的 Thread S ,存储在 Map .

    又一次,这种干扰从未发生过。 AtomicBoolean 现在控制线程的执行。

    private AtomicBoolean stopped = new AtomicBoolean(false);
    
     @Override
        public void run() {
            while (!stopped.get()) {
                readUrl();
            }
    }
    
    public void stopExecution() {
            stopped.set(true);
    }
    

    这是一个绝望的举动,但迄今为止唯一有效的。