停止无限运行的线程

时间:2015-07-13 23:27:02

标签: java multithreading interrupt producer-consumer interrupted-exception

我一直在尝试实现生产者消费模式。如果生产者和消费者都无限期地运行,那么应该如何阻止它们呢?

我一直在尝试测试isInterrupted()的状态,但以下代码并不能保证所有线程都停止。

public class Monitor {

    private int value;
    private boolean readable = false;

    public synchronized void setVal(int value) {
        while (readable) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                break;
            }
        }

        if (!Thread.currentThread().isInterrupted()) {
            this.readable = true;
            this.value = value;
            this.notifyAll();
        }
    }

    public synchronized int getVal() {
        while (!readable) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                break;
            }
        }
        if (!Thread.currentThread().isInterrupted()) {
            this.readable = false;
            this.notifyAll();
        }
        return this.value;
    }
}

生产者类看起来像这样:

import java.util.Random;

public class Producer implements Runnable {

    private Monitor monitor;
    private Random r = new Random();
    private String name;

    public Producer(Monitor m) {monitor = m;}

    public void run() {
        name = Thread.currentThread().getName();
        while (!Thread.currentThread().isInterrupted()) {
            int value = r.nextInt(1000);
            monitor.setVal(value);
            System.out.println("PRODUCER: " + name + " set " + value);
        }
        System.out.println("PRODUCER: " + name + " interrupted");
    }
}

消费者

public class Consumer implements Runnable {

    private Monitor monitor;
    private String name;

    public Consumer(Monitor m) {monitor = m;}

    public void run() {
        name = Thread.currentThread().getName();
        while (!Thread.currentThread().isInterrupted()) {
            int value = monitor.getVal();
            System.out.println("CONSUMER: " + name + " got " + value);
        }
        System.out.println("CONSUMER: " + name + " interrupted");
    }
}

主要:

public class Main {

    public static void main(String[] args) {

        final int n = 2;
        Monitor m = new Monitor();

        Thread[] producers = new Thread[n];
        Thread[] consumers = new Thread[n];

        for (int i = 0; i < n; i++) {
            producers[i] = new Thread(new Producer(m));
            producers[i].start();
            consumers[i] = new Thread(new Consumer(m));
            consumers[i].start();
        }

//        try {
//            Thread.sleep(1);
//        } catch (InterruptedException e) {}

        for (int i = 0; i < n; i++) {
            producers[i].interrupt();
            consumers[i].interrupt();
        }
    }
}

我得到以下结果

PRODUCER: Thread-0 set 917
CONSUMER: Thread-1 got 917
PRODUCER: Thread-2 set 901
PRODUCER: Thread-0 set 29
CONSUMER: Thread-3 interrupted
CONSUMER: Thread-1 got 29
CONSUMER: Thread-1 interrupted
PRODUCER: Thread-2 set 825
...program hangs

PRODUCER: Thread-0 set 663
CONSUMER: Thread-1 got 663
PRODUCER: Thread-0 set 129
CONSUMER: Thread-1 got 129
PRODUCER: Thread-2 set 93
PRODUCER: Thread-2 interrupterd
CONSUMER: Thread-3 interrupted
PRODUCER: Thread-0 set 189
PRODUCER: Thread-0 interrupterd
CONSUMER: Thread-1 got 129
...program hangs
等等......

显然有些不对劲。为什么我没有一致地注册interrupt的呼叫?

1 个答案:

答案 0 :(得分:2)

捕获InterruptedException 并不意味着在线程上设置了中断标志也许是令人惊讶的。

中断标志和InterruptedException是两种完全独立的方式,表示发生了中断:

  • 您可以抛出InterruptedException而无需先检查线程是否已被中断。
  • 您可以设置中断的标记,而不会捕获InterruptedException

为了正确地保留线程被中断的事实(或者至少,被捕获InterruptedException),你应该显式地重新中断catch块中的线程:

//  ...
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  break;
}
// ...

这会设置中断的标记,因此您的isInterrupted()检查现在可以正常工作。