在Java中中断循环线程

时间:2011-08-11 11:40:23

标签: java multithreading interrupted-exception

我正在尝试理解线程如何在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");
}

我创建的主题 indended 迟早会中断。所以,我检查了isInterrupted()标志来判断我是否需要继续操作并在我进行某种等待操作(InterruptedExceptionsleep时捕获join来处理这些情况, wait)。

我想澄清的事情是:

  1. 使用中断机制进行此类任务是否可以? (与volatile boolean shouldStop
  2. 相比
  3. 此解决方案是否正确?
  4. 我吞下InterruptedException是否正常?我真的不感兴趣的是有人要求我的线程中断的代码片段。
  5. 有没有更短的方法来解决这个问题? (重点是'无限'循环)
  6. 修改interrupt()的catch中添加了对InterruptedException的调用。

4 个答案:

答案 0 :(得分:4)

我回答不是。 3:

基本上问题是:中断异常的目的是什么?它告诉你停止阻止(例如睡觉)并提前返回。

处理InterruptedException有两种方法:

  • 重新抛出它,所以线程仍然中断
  • 再次设置Thread.currentThread.interrupt()并进行清理工作。通过这种方式,您可以确保线程中的另一个方法开始重新启动

简单地吞咽InterruptedException对于最终终止的中断的目的并不是一个好主意。但是你只被要求打断,所以你还有时间清理。

在这种情况下,这可能是我自己的“过度反应”,但通常这样的代码要复杂得多,你怎么知道,这个Thread中的一些后续代码不会再次调用阻塞方法?

修改

否则我认为你所做的很好。但是对我来说有点令人惊讶,因为我从未在他自己的代码中看到任何人实际上这样做。

有趣的文章解释了为什么可以在这里找到:http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html

答案 1 :(得分:2)

  1. 是的,没关系。您应该记录必须如何停止Thread / Runnable。您可以在Runnable实现上添加一个专用的stop方法来封装停止机制。要么使用中断,要么使用专用的布尔值,或两者都使用。
  2. 是的,除了好的做法是在捕获InterruptedException时恢复中断状态:Thread.currentThread().interrupt();
  3. 不,您应该恢复中断状态
  4. 没有我知道

答案 2 :(得分:1)

1)根据 Java Concurrency in Practice 一书,你的例子中的方法比使用易失性标志(因为你已经有中断的标志是冗余的)更可取。这是InterruptedExceptions的使用方式。

2)是的

3)只要恢复中断标志状态,就可以吃异常。该例外不代表错误,所以吃掉它不会丢失任何信息,它纯粹是一种转移控制的手段。 (恢复中断标志状态对于你有嵌套控制结构的情况很重要,每个嵌套控制结构都需要通知线程正在取消,对于一个简单的例子,像你的它是好的形式但是如果它缺失它不会伤害任何东西。)< / p>

4)没有

答案 3 :(得分:1)

使用中断很好,但使用它们很好。你必须重新抛出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();
    }
}