Не может прерывать задачи ExecutorService - PullRequest
0 голосов
/ 29 января 2019

Редактировать:

Чтобы проверить эту проблему вне среды 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 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();
     }
}

Наконец вот AttackScript класс:

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

Странная часть в том, что редко, как 1 из 10, задачи прерываются и выполнение AttackScript останавливается.Но остальные 9 задач не были прерваны, продолжая openStreams () на URL с.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Вы уже ответили правильным решением, чтобы избежать этой проблемы, но я объясню причину.Ошибка не в 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(), где это возможно, вместо этого.

0 голосов
/ 01 февраля 2019

Вынужден найти альтернативное решение. Я полностью удалил использование пула потоков и теперь реализую single Thread s , хранится в Map.

Прерывание, опять же, никогда не происходило, поэтому AtomicBoolean теперь контролирует выполнение потока.

private AtomicBoolean stopped = new AtomicBoolean(false);

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

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

Это отчаянный ход, нопока только один, который работает.

...