6

我试图了解线程在 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. 有没有更短的方法来解决这个问题?(重点是有“无限”循环)

编辑interrupt()在 catch 中 添加了对InterruptedException.

4

4 回答 4

4

我回答不。3:

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

处理 InterruptedException 有两种方法:

  • 重新抛出它,所以线程保持中断
  • 重新设置Thread.currentThread.interrupt()并进行清理工作。这样,您可以确定线程中开始休眠的另一个方法将再次抛出

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

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

编辑

否则我认为你正在做的很好。不过,对我来说有点令人惊讶,因为我从未见过任何人在他自己的代码中真正做到这一点。

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

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

1) 根据Java Concurrency in Practice一书,您的示例中的方式优于使用 volatile 标志(这是多余的,因为您已经有了中断标志)。这就是 InterruptedExceptions 的用途。

2) 是的

3)只要恢复中断标志状态就可以吃异常。异常并不代表错误,因此吃掉它不会丢失任何信息,它纯粹是一种转移控制的手段。(恢复中断标志状态对于您有嵌套控制结构的情况很重要,每个控制结构都需要被告知线程正在取消,对于像您这样的简单示例,这是一种很好的形式,但如果它丢失它不会有任何伤害。)

4) 没有

于 2011-08-11T12:26:41.057 回答
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。然后你可以使用 (for some) 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();
    }
}
于 2011-08-11T12:29:46.057 回答