Java 即使在调用中断后线程也没有停止
Java thread not stopping even after calling interrupt
我在第 370 天调用中断,然后在其他 classes 的 运行 方法期间的 catch 块中再次调用它。我还有一个 while 条件,它在线程未中断时循环,但由于某种原因,它不起作用,我不知道为什么。我知道我可以改用变量标志,但我想尝试使 interrupt() 起作用。我已经看过多个网站,但 none 似乎对我有用。请帮忙
public class Elf implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// wait a day
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public class Main {
public static void main(String args[]) {
Scenario scenario = new Scenario();
// create the participants
// Santa
scenario.setSanta( new Santa(scenario) );
Thread th = new Thread(scenario.getSanta());
th.start();
// The elves: in this case: 10
for (int i = 0; i != 10; i++) {
Elf elf = new Elf(i + 1, scenario);
scenario.getElves().add(elf);
th = new Thread(elf);
th.start();
}
// The reindeer: in this case: 9
for (int i = 0; i != 9; i++) {
Reindeer reindeer = new Reindeer(i + 1, scenario);
scenario.getReindeers().add(reindeer);
th = new Thread(reindeer);
th.start();
}
// now, start the passing of time
for (int day = 1; day < 500; day++) {
// wait a day
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// turn on December
if (day > (365 - 31)) {
scenario.setDecember(true);
}
// interrupt flag is set here
if (day == 370) {
th.interrupt();
}
// print out the state:
System.out.println("*********** Day " + day
+ " *************************");
scenario.getSanta().report();
for (Elf elf : scenario.getElves()) {
elf.report();
}
for (Reindeer reindeer : scenario.getReindeers()) {
reindeer.report();
}
}
}
}
我这里只包含了Elf class,但是其他class都是相同的结构,里面的代码几乎相同。现在程序结束时红色方块(终止按钮)仍然亮着,我读到这表明仍有线程 运行ning。我不确定为什么它没有停止。
声明为 Thread th
的变量是对类型 Thread
对象的引用。它只是对一个对象的引用。顺便说一下,对于所有类型都是如此,而不仅仅是线程。
每当您将新值放入变量时,它不再引用旧值[1].
因此,您的代码中的 th.interrupt()
只会中断您分配给它的最后一个线程 - 最近的驯鹿线程。
如果要中断所有线程,则需要保留对所有线程的引用。
一个基本的解决方案
一个简单的方法是使用 Thread
:
的列表
List<Thread> allThreadsToInterrupt = new ArrayList<>();
当你创建一个线程时,你会做
th = new Thread(...);
allThreadsToInterrupt.add(th);
最后你可以做:
for ( Thread th: allThreadsToInterrupt ) {
th.interrupt();
}
使用 ThreadGroup
但事实上,Java 有一个现有的 class 可以帮助您解决这个问题 - ThreadGroup
class。你可以这样做:
ThreadGroup elfThreads = new ThreadGroup("Elf Threads");
ThreadGroup reindeerThreads = new ThreadGroup( "Reindeer Threads" );
现在,无论何时创建线程,都应该使用线程组来创建它:
而不是:
th = new Thread(elf);
使用:
th = new Thread(elfThreads,elf);
然后在最后打断所有精灵,可以运行:
elfThreads.interrupt();
这将在属于该组的所有线程上自动调用interrupt()
。
当然,你可以只创建一个大群,但我演示了将精灵和驯鹿分开,以防你需要将它们分开打断。
[1] 在大多数情况下,用新引用替换旧引用(对象的唯一引用)将导致旧对象符合垃圾回收条件,但线程略有不同,因为如果它们已经启动,当前线程组中有对它们的第二个引用(因为当您在创建线程时不提供线程组时这是默认设置),这意味着它们不会被垃圾收集并且它们将 运行 正确地完成,直到它们完成。
我在第 370 天调用中断,然后在其他 classes 的 运行 方法期间的 catch 块中再次调用它。我还有一个 while 条件,它在线程未中断时循环,但由于某种原因,它不起作用,我不知道为什么。我知道我可以改用变量标志,但我想尝试使 interrupt() 起作用。我已经看过多个网站,但 none 似乎对我有用。请帮忙
public class Elf implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// wait a day
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public class Main {
public static void main(String args[]) {
Scenario scenario = new Scenario();
// create the participants
// Santa
scenario.setSanta( new Santa(scenario) );
Thread th = new Thread(scenario.getSanta());
th.start();
// The elves: in this case: 10
for (int i = 0; i != 10; i++) {
Elf elf = new Elf(i + 1, scenario);
scenario.getElves().add(elf);
th = new Thread(elf);
th.start();
}
// The reindeer: in this case: 9
for (int i = 0; i != 9; i++) {
Reindeer reindeer = new Reindeer(i + 1, scenario);
scenario.getReindeers().add(reindeer);
th = new Thread(reindeer);
th.start();
}
// now, start the passing of time
for (int day = 1; day < 500; day++) {
// wait a day
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// turn on December
if (day > (365 - 31)) {
scenario.setDecember(true);
}
// interrupt flag is set here
if (day == 370) {
th.interrupt();
}
// print out the state:
System.out.println("*********** Day " + day
+ " *************************");
scenario.getSanta().report();
for (Elf elf : scenario.getElves()) {
elf.report();
}
for (Reindeer reindeer : scenario.getReindeers()) {
reindeer.report();
}
}
}
}
我这里只包含了Elf class,但是其他class都是相同的结构,里面的代码几乎相同。现在程序结束时红色方块(终止按钮)仍然亮着,我读到这表明仍有线程 运行ning。我不确定为什么它没有停止。
声明为 Thread th
的变量是对类型 Thread
对象的引用。它只是对一个对象的引用。顺便说一下,对于所有类型都是如此,而不仅仅是线程。
每当您将新值放入变量时,它不再引用旧值[1].
因此,您的代码中的 th.interrupt()
只会中断您分配给它的最后一个线程 - 最近的驯鹿线程。
如果要中断所有线程,则需要保留对所有线程的引用。
一个基本的解决方案
一个简单的方法是使用 Thread
:
List<Thread> allThreadsToInterrupt = new ArrayList<>();
当你创建一个线程时,你会做
th = new Thread(...);
allThreadsToInterrupt.add(th);
最后你可以做:
for ( Thread th: allThreadsToInterrupt ) {
th.interrupt();
}
使用 ThreadGroup
但事实上,Java 有一个现有的 class 可以帮助您解决这个问题 - ThreadGroup
class。你可以这样做:
ThreadGroup elfThreads = new ThreadGroup("Elf Threads");
ThreadGroup reindeerThreads = new ThreadGroup( "Reindeer Threads" );
现在,无论何时创建线程,都应该使用线程组来创建它:
而不是:
th = new Thread(elf);
使用:
th = new Thread(elfThreads,elf);
然后在最后打断所有精灵,可以运行:
elfThreads.interrupt();
这将在属于该组的所有线程上自动调用interrupt()
。
当然,你可以只创建一个大群,但我演示了将精灵和驯鹿分开,以防你需要将它们分开打断。
[1] 在大多数情况下,用新引用替换旧引用(对象的唯一引用)将导致旧对象符合垃圾回收条件,但线程略有不同,因为如果它们已经启动,当前线程组中有对它们的第二个引用(因为当您在创建线程时不提供线程组时这是默认设置),这意味着它们不会被垃圾收集并且它们将 运行 正确地完成,直到它们完成。