Looking for java Keywords? Try Ask4Keywords

Java Language Потоки прерывания потока / остановки


пример

Каждый поток Java имеет флаг прерывания, который изначально ошибочен. Прерывание потока, по сути, не более чем установка этого флага в true. Код, выполняющийся на этом потоке, может иногда проверять флаг и действовать на него. Код также может полностью игнорировать его. Но почему каждый поток имеет такой флаг? В конце концов, наличие булевого флага в потоке - это то, что мы можем просто организовать, если и когда нам это нужно. Ну, есть методы, которые ведут себя особым образом, когда поток, в котором они работают, прерывается. Эти методы называются методами блокировки. Это методы, которые помещают поток в состояние WAITING или TIMED_WAITING. Когда поток находится в этом состоянии, прерывая его, будет выведено прерывание Exception на прерванный поток, вместо того, чтобы флаг прерывания был установлен в true, и поток снова станет RUNNABLE. Код, который вызывает метод блокировки, вынужден иметь дело с InterruptedException, поскольку это проверенное исключение. Таким образом, и, следовательно, его имя, прерывание может иметь эффект прерывания WAIT, эффективно заканчивая его. Обратите внимание, что не все методы, которые каким-то образом ждут (например, блокирование IO), реагируют на прерывание таким образом, поскольку они не помещают поток в состояние ожидания. Наконец, поток, у которого установлен флаг прерывания, который вводит метод блокировки (т. Е. Пытается попасть в состояние ожидания), немедленно выдаст исключение InterruptedException, и флаг прерывания будет очищен.

Помимо этой механики, Java не назначает никакого специального семантического значения прерывания. Код может интерпретировать прерывание любым способом, который ему нравится. Но чаще всего прерывание используется для подачи сигнала в поток, который он должен прекратить работать в кратчайшие сроки. Но, как должно быть ясно из вышесказанного, для того, чтобы прекратить работу, отреагировать на это прерывание будет только код этого потока. Остановка потока - это сотрудничество. Когда поток прерывается, его код запуска может находиться на несколько уровней в стеке. Большая часть кода не вызывает метод блокировки и заканчивается достаточно своевременно, чтобы не задерживать остановку потока чрезмерно. Код, который должен в основном касаться реагирования на прерывание, - это код, который находится в задачах обработки цикла, пока их не осталось, или пока флаг не будет установлен, чтобы сигнализировать об этом, чтобы остановить этот цикл. Циклы, которые обрабатывают, возможно, бесконечные задачи (т. Е. Продолжают работать в принципе), должны проверять флаг прерывания, чтобы выйти из цикла. Для конечных циклов семантика может диктовать, что все задачи должны быть закончены до окончания или может быть уместно оставить некоторые необработанные задачи. Код, который вызывает методы блокировки, будет вынужден иметь дело с InterruptedException. Если это вообще возможно семантически, оно может просто распространять InterruptedException и объявлять его бросить. Таким образом, он становится методом блокировки в отношении своих вызывающих абонентов. Если он не может распространять исключение, он должен по крайней мере установить прерванный флаг, поэтому вызывающие выше столбцы также знают, что поток был прерван. В некоторых случаях метод должен продолжать ожидание независимо от InterruptedException, и в этом случае он должен задерживать установку прерванного флага до тех пор, пока он не будет завершен, это может включать настройку локальной переменной, которая должна быть проверена до выхода из метода затем прервите его поток.

Примеры :

Пример кода, который прекращает обработку задач при прерывании

class TaskHandler implements Runnable {
    
    private final BlockingQueue<Task> queue;

    TaskHandler(BlockingQueue<Task> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) { // check for interrupt flag, exit loop when interrupted
            try {
                Task task = queue.take(); // blocking call, responsive to interruption
                handle(task);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // cannot throw InterruptedException (due to Runnable interface restriction) so indicating interruption by setting the flag
            }
        }
    }
    
    private void handle(Task task) {
        // actual handling
    }
}

Пример кода, который задерживает установку флага прерывания до полного завершения:

class MustFinishHandler implements Runnable {

    private final BlockingQueue<Task> queue;

    MustFinishHandler(BlockingQueue<Task> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        boolean shouldInterrupt = false;
        
        while (true) {
            try {
                Task task = queue.take();
                if (task.isEndOfTasks()) {
                    if (shouldInterrupt) {
                        Thread.currentThread().interrupt();
                    }
                    return;
                }
                handle(task);
            } catch (InterruptedException e) {
                shouldInterrupt = true; // must finish, remember to set interrupt flag when we're done
            }
        }
    }

    private void handle(Task task) {
        // actual handling
    }
}

Пример кода, который имеет фиксированный список задач, но может быть прекращен раньше, когда прерван

class GetAsFarAsPossible implements Runnable {

    private final List<Task> tasks = new ArrayList<>();

    @Override
    public void run() {
        for (Task task : tasks) {
            if (Thread.currentThread().isInterrupted()) {
                return;
            }
            handle(task);
        }
    }

    private void handle(Task task) {
        // actual handling
    }
}