Прерывание зацикленных потоков в Java - PullRequest
5 голосов
/ 11 августа 2011

Я пытаюсь понять, как работают потоки в Java, и сейчас изучаю, как реализовать зацикленные потоки, которые можно отменить. Вот код:

public static void main(String[] args) throws Exception {
    Thread t = new Thread() {
        @Override
        public void run() {
            System.out.println("THREAD: started");
            try {
                while(!isInterrupted()) {
                    System.out.printf("THREAD: working...\n");
                    Thread.sleep(100);
                }
            } catch(InterruptedException e) {
                // we're interrupted on Thread.sleep(), ok

                // EDIT
                interrupt();

            } finally {
                // we've either finished normally
                // or got an InterruptedException on call to Thread.sleep()
                // or finished because of isInterrupted() flag

                // clean-up and we're done
                System.out.println("THREAD: done");
            }               
        }
    };

    t.start();
    Thread.sleep(500);
    System.out.println("CALLER: asking to stop");
    t.interrupt();
    t.join();
    System.out.println("CALLER: thread finished");
}

Поток, который я создаю, - с отступом , который рано или поздно прерывается. Итак, я проверяю флаг isInterrupted (), чтобы решить, нужно ли мне продолжать, а также перехватывать InterruptedException для обработки случаев, когда я нахожусь в какой-то операции ожидания (sleep, join, wait).

Я хотел бы уточнить:

  1. Можно ли использовать механизм прерывания для такого рода задач? (по сравнению с volatile boolean shouldStop)
  2. Правильно ли это решение?
  3. Это нормально, что я проглатываю InterruptedException? Мне не очень интересно, что это был за фрагмент кода, где кто-то попросил прерывать мой поток.
  4. Есть ли более короткие способы решения этой проблемы? (главное - это бесконечный цикл)

EDIT Добавлен вызов на interrupt() в улове для InterruptedException.

Ответы [ 4 ]

4 голосов
/ 11 августа 2011

Я отвечаю нет.3:

По сути, вопрос заключается в следующем: какова цель исключения Interrupted?Он говорит вам прекратить блокировку (например, спящий режим) и вернуться рано.

Существует два способа работы с InterruptedException:

  • Снова обработать его, поэтому поток остается прерванным
  • установите Thread.currentThread.interrupt() снова и выполните работу по очистке.Таким образом, вы можете быть уверены, что другой метод в вашем потоке, который начинает спать снова, выдает

Простое проглатывание InterruptedException не является хорошей идеей относительно цели такого прерывания, которое должно окончательно завершиться,Но вас просят только прервать, так что у вас все еще есть время на очистку.

В этом случае это может быть «чрезмерной реакцией» на меня, но обычно такой код гораздо сложнее, и откуда вы знаете, чтокакой-нибудь дополнительный код в этой теме не вызовет метод блокировки снова?

РЕДАКТИРОВАТЬ

В противном случае я думаю, что вы делаете хорошо.Для меня немного удивительно, хотя, потому что я никогда не видел, чтобы кто-то в его собственном коде действительно делал это.

И интересная статья, объясняющая, почему можно найти здесь: http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html

2 голосов
/ 11 августа 2011
  1. Да, все в порядке.Вы должны документально подтвердить, как должен быть остановлен Thread / Runnable.Вы можете добавить выделенный метод stop в вашу реализацию Runnable, которая инкапсулирует механизм остановки.Либо используйте прерывание, либо используйте выделенное логическое значение, либо оба.
  2. Да, за исключением того, что рекомендуется восстанавливать состояние прерывания при перехвате InterruptedException: Thread.currentThread().interrupt();
  3. Нет, вам следует восстановитьстатус прерывания
  4. Нет того, о чем я знаю
1 голос
/ 11 августа 2011

Хорошо использовать прерывание, но используйте его хорошо. Вы должны повторно бросить Thread.currentThread().interrupt() в свой улов. Вот фрагмент кода, показывающий, почему:

public class MyThread extends Thread {
    private static boolean correct = true;

    @Override
    public void run() {
        while (true) {
            // Do Something 1
            for (int i = 0; i < 10; i++) { // combined loop
                // Do Something 2
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    if (correct)
                        Thread.currentThread().interrupt(); // reinterrupting
                    System.out.println("First Catch");
                    break; // for
                }
            }
            try {
                // Do Something 3
                System.out.print("before sleep, ");
                Thread.sleep(1000);
                System.out.print("After sleep, ");
            } catch (InterruptedException ex) {
                if (correct)
                    Thread.currentThread().interrupt();
                System.out.println("Second catch");
                break; // while
            }
        }
        System.out.println("Thread closing");
    }

    private static void test() throws InterruptedException {
        Thread t = new MyThread();
        t.start();
        Thread.sleep(2500);
        t.interrupt();
        t.join();
        System.out.println("End of Thread");
    }

    public static void main(String[] args)
            throws InterruptedException {
        test();
        correct = false; // test "bad" way
        test();
    }
}

Другое дело, Interruptions не всегда работает при ожидании на InputStreams. Затем вы можете использовать (для некоторых) InterruptedIOException, но это не всегда будет работать. Чтобы понять эти случаи, вы можете попробовать этот фрагмент кода:

public class Mythread extends Thread {
    private InputStream in;

    public Mythread(InputStream in) {
        this.in = in;
    }

    @Override
    public void interrupt() {
        super.interrupt();
        try {
            in.close(); // Close stream if case interruption didn't work
        } catch (IOException e) {}
    }

    @Override
    public void run() {
        try {
            System.out.println("Before read");
            in.read();
            System.out.println("After read");
        } catch (InterruptedIOException e) { // Interruption correctly handled
            Thread.currentThread().interrupt();
            System.out.println("Interrupted with InterruptedIOException");
        } catch (IOException e) {
            if (!isInterrupted()) { // Exception not coming from Interruption
                e.printStackTrace();
            } else { // Thread interrupted but InterruptedIOException wasn't handled for this stream
                System.out.println("Interrupted");
            }
        }
    }

    public static void test1() // Test with socket
            throws IOException, InterruptedException {
        ServerSocket ss = new ServerSocket(4444);
        Socket socket = new Socket("localhost", 4444);
        Thread t = new Mythread(socket.getInputStream());
        t.start();
        Thread.sleep(1000);
        t.interrupt();
        t.join();
    }

    public static void test2() // Test with PipedOutputStream
            throws IOException, InterruptedException { 
        PipedInputStream in = new PipedInputStream(new PipedOutputStream());
        Thread t = new Mythread(in);
        t.start();
        Thread.sleep(1000);
        t.interrupt();
        t.join();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        test1();
        test2();
    }
}
1 голос
/ 11 августа 2011

1) Способ в вашем примере предпочтительнее использования флага volatile (который является избыточным, поскольку у вас уже есть флаг прерывания), согласно книге Java Concurrency in Practice . Именно так и предполагалось использовать InterruptedExceptions.

2) Да

3) исключение можно использовать до тех пор, пока вы восстанавливаете состояние флага прерывания. Исключение не представляет собой ошибку, поэтому его употребление не приводит к потере информации, это просто средство передачи контроля. (Восстановление статуса флага прерывания важно в тех случаях, когда у вас есть вложенные управляющие структуры, каждая из которых должна быть проинформирована о том, что поток отменяется, для простого примера, такого как ваш, это хорошая форма, но если он отсутствует, это не повредит. 1007 *

4) нет

...