我有一些麻烦要理解我必须停止正在运行的线程。我会试着通过例子来解释它。假设以下类:
public class MyThread extends Thread {
protected volatile boolean running = true;
public void run() {
while (running) {
synchronized (someObject) {
while (someObject.someCondition() == false && running) {
try {
someObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// do something useful with someObject
}
}
}
public void halt() {
running = false;
interrupt();
}
}
假设线程正在运行,并且以下语句被评估为true:
while (someObject.someCondition() == false && running)
然后,另一个线程调用MyThread.halt()。尽管此函数将'running'设置为false(这是一个易失性布尔值)并中断线程,但仍然执行以下语句:
someObject.wait();
我们陷入僵局。线程永远不会停止。
然后我想出了这个,但我不确定它是否正确:
public class MyThread extends Thread {
protected volatile boolean running = true;
public void run() {
while (running) {
synchronized (someObject) {
while (someObject.someCondition() == false && running) {
try {
someObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// do something useful with someObject
}
}
}
public void halt() {
running = false;
synchronized(someObject) {
interrupt();
}
}
}
这是对的吗?这是最常用的方法吗?
这似乎是一个显而易见的问题,但我没有提出解决方案。非常感谢你的帮助。
答案 0 :(得分:2)
interrupt()调用将在被中断的线程中设置一个标志,someObject.wait()将always检查这个标志,所以你的第一个类应该工作。 AFAIC第一种是常见的方式,你的错误必须在其他地方。
答案 1 :(得分:1)
我试图模拟第一个版本,实际上,中断的标志会被记住。我不知道。这是我的代码如何模拟它:
public class Test {
protected static class MyThread extends Thread {
protected Object someObject = new Object();
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; ++i) {
/* this takes some time */
}
try {
synchronized (someObject) {
someObject.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("WE COME HERE AFTER INTERRUPTED EXCEPTION");
try {
synchronized (someObject) {
someObject.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("HOWEVER, WE NEVER COME HERE, THE INTERRUPTED FLAG SEEMS TO BE RESETTED");
}
public void halt() {
interrupt();
}
}
public static void main(String[] a) throws InterruptedException {
MyThread t = new MyThread();
t.start();
t.halt();
}
}
这让我感到疑惑。为什么
public void halt() {
interrupt();
}
首选
public void halt() {
synchronized(someObject) {
someObject.notifyAll();
}
}
在两个版本中,while(...)将再次评估?
答案 2 :(得分:0)
在catch
的{{1}}区块中,您应该包含:
InterruptedException
并将if (!running)
break main;
循环标记为while (running)
无需在main: while (running)
上进行同步即可拨打someObject
。
另外,我建议您重命名interrupt
变量,因为它非常混乱。我建议running
。因此:
shouldContinue
答案 3 :(得分:0)
或者....你可以使用重载等待方法,它将输入的最大毫秒数等待。现在,在您的halt
方法中,您可以设置running=false
并保证在指定的毫秒后等待调用将结束,同时将再次评估条件。
在下面的修改代码中,等待将阻止不超过1秒,然后再次检查while循环条件。这次运行设置为false,然后循环结束。
synchronized (someObject) {
while (someObject.someCondition() == false && running) {
try {
someObject.wait(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
注意:这种方法在高性能线程中可能不是最好的,但我发现它在很多情况下都很有用。
BTW - 如果JVM正在执行基本上非阻塞(不可中断)的逻辑,例如代码中的while循环条件,则然后由halt方法触发中断将'迷失'。而且您必须依赖'isInterrupted'标志来告诉逻辑是否调用了中断。