线程和中断
Threads and interrupt
我有以下代码
package threads;
import java.util.ArrayList;
import java.util.List;
public class Threads extends Thread implements Runnable {
private final List<Thread> threadList = new ArrayList<>();
private String f, l;
private Thread greetings1, greetings;
public static void main(String[] args) {
String[] elements = { "Tim", "Fred", "Jeff", "Scott" };
Threads t = new Threads();
for (String e : elements) {
t.threadL(e);
t.threadR(e);
}
for (int index = 0; index < t.threadList.size(); index++) {
// blank at the moment
}
}
public void threadR(String f) {
greetings = new Thread(f);
Thread greetingsFromFred = new Thread(greetings) {
@Override
public void run() {
for (int i = 1; i < 5; i++) {
try {
System.out.println("Main Thread: " + i + " " + f);
Thread.sleep(5000);
} catch (InterruptedException ex) {
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread exiting.");
}
}
};
greetingsFromFred.start();
}
public List<Thread> threadL(String l) {
greetings1 = new Thread(this);
this.l = l;
greetings1.start();
threadList.add(greetings1);
return (threadList);
}
@Override
public void run() {
greetings.interrupt(); // interrupt greetings thread here <-------
System.out.println("Greetings from " + l + "! threadL");
}
}
当 threadL 运行时,我想从 运行 中断 threadR,因此导致
System.out.println("Main thread interrupted.");
将从 threadR 打印
我在代码中突出显示了应该发生中断的位置
greetings.interrupt(); //interrupt greetings thread here <-------
为什么中断不能正常工作?
您需要打断 greetingsFromFred
。这是正在启动的线程,实际上是 运行。线程 greetings
永远不会 运行。 Is 仅作为 运行nable 传递给另一个线程。为什么还要在线程的构造函数中传递其他线程?这整个设计很可疑。我认为您对 thread/runnable 以及何时以及如何使用感到困惑。
您要中断的线程称为 greetingsFromFred
而不是 greetings
,因此只要您使 greetingsFromFred
可见,此更改就会起作用。
greetingsFromFred.interrupt(); //interrupt greetings thread here <-------
您的程序有点像线程对象的迷宫。让我们尝试从 main
.
跟随它
在 main
中,您创建了一个类型为 Threads
的对象 t
。这意味着它是 Thread
和 Runnable
.
然后,对于您的每个字符串,您 运行 t
的 threadL
和 threadR
将该字符串作为参数。
在 main
的任何地方你都没有真正开始 t
,甚至 运行 它的 run
方法直接开始。
然后,在 threadR
中创建一个新的空 Thread
并将其分配给 greetings
。
然后您使用那个新的空 Thread
(问候)作为一个 Runnable
,它被传递给一个新的 Thread
对象,该对象的 run
方法也被覆盖。这是徒劳的。在任何情况下,greetingsFromFred
都会启动,并且会 运行 循环。但是 greetings
仍然包含与 greetingsFromFred
线程无关的空 Thread
。
在 threadL
中,您创建了另一个 Thread
,将当前的 Thread
(即 main
中的 t
)作为其 运行启用。然后你终于开始了。它将尝试中断 greetings
中的线程,但正如我们所说,这是一个从未启动的非活动空线程。
你的结构应该没有那么复杂。尽可能只使用 Runnable
对象来处理应该执行的事情。因此,Threads
本身应该是 Runnable
,而 threadR
应该固定为这样的东西:
public void threadR(String f) {
Runnable greetingsFromFred = new Runnable() {
@Override
public void run() {
for (int i = 1; i < 5; i++) {
try {
System.out.println("Main Thread: " + i + " " + f);
Thread.sleep(5000);
} catch (InterruptedException ex) {
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread exiting.");
}
}
};
greetings = new Thread(greetingsFromFred, f);
greetings.start();
}
这样,greetings
将是一个 Thread
,它将 运行 您在 Runnable
[=70= 中创建的 循环],还有名字 f
.
但是请注意:由于您实际上并没有在 catch
子句中停止循环,因此当线程被中断时您不会退出循环,并且您可能会得到 "Exiting" 消息打印不止一次,具体取决于中断何时进入循环。
正如其他人所指出的,这里有几个问题。
不必要的继承
您的 class Threads
有一个 run()
方法可以中断分配给 greetings
的任何线程,但 R
和 L
都不会继承 run()
方法。 R
扩展了 Thread
并覆盖了 run()
,而 L
只是扩展了 Thread
而没有覆盖。这种结构没有意义。
鉴于此,run()
方法似乎应该被 L
覆盖。
如果您做到了,那么 Threads
就没有必要覆盖 Thread
或实施 Runnable
。您可以完全取消它并让 R
和 L
各自覆盖 Thread
(使用 Thread(String)
构造函数)。
public class Threads {
...
public List<Thread> threadL(String l) {
greetings1 = new Thread(l) {
@Override
public void run() {
greetings.interrupt(); // interrupt greetings thread here <-------
System.out.println("Greetings from " + l + "! threadL");
}
...
}
}
所以现在 threadR()
创建并启动一个将被中断的新 Thread
,并且 threadL()
创建并启动一个将中断的新 Thread
,并且 Threads
仅存在于 main()
方法和 threadR()
和 threadL()
中的线程 creation/starting。清洁工
中断错误的线程
正如其他人所指出的,您有这个不必要的变量 greetingsFromFred
,这只会使事情复杂化。你为 greetingsFromFred
所做的一切你应该为 greetings
做(构造函数除外)。
public void threadR(String f) {
greetings = new Thread(f) {
@Override
public void run() {
...
}
greetings.start();
}
greetings
上的竞争条件
这是最微妙的部分,IMO。您正在使用静态变量 greetings
来保存当前 Thread
对象 R
,当前 L
应该中断该对象。但是这两个线程之间没有协调。没有理由相信每个 L
都会在 greetings
中看到一个独特的 R
。这是一件非常糟糕的事情。你需要做的是让 greetings
volatile
,然后让 R
等到 greetings
为空再赋值,让 L
等到 greetings
] 在中断之前不为空。
private volatile Thread greetings;
...
public void threadR(String f) {
while (greetings != null);
greetings = new Thread(f) {
...
public void threadL(String l) {
Thread greetings1 = new Thread(l) {
@Override
public void run() {
while (greetings == null);
...
注意 while(...);
的用法。尾随的分号是一个空语句。这个结构只是说继续检查条件直到它为假,然后继续。由于 R
直到它有一个 null
然后设置它才会继续,并且 L
永远不会继续直到它有一个 non-null
然后将其置空,它们将始终保持同步.
我有以下代码
package threads;
import java.util.ArrayList;
import java.util.List;
public class Threads extends Thread implements Runnable {
private final List<Thread> threadList = new ArrayList<>();
private String f, l;
private Thread greetings1, greetings;
public static void main(String[] args) {
String[] elements = { "Tim", "Fred", "Jeff", "Scott" };
Threads t = new Threads();
for (String e : elements) {
t.threadL(e);
t.threadR(e);
}
for (int index = 0; index < t.threadList.size(); index++) {
// blank at the moment
}
}
public void threadR(String f) {
greetings = new Thread(f);
Thread greetingsFromFred = new Thread(greetings) {
@Override
public void run() {
for (int i = 1; i < 5; i++) {
try {
System.out.println("Main Thread: " + i + " " + f);
Thread.sleep(5000);
} catch (InterruptedException ex) {
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread exiting.");
}
}
};
greetingsFromFred.start();
}
public List<Thread> threadL(String l) {
greetings1 = new Thread(this);
this.l = l;
greetings1.start();
threadList.add(greetings1);
return (threadList);
}
@Override
public void run() {
greetings.interrupt(); // interrupt greetings thread here <-------
System.out.println("Greetings from " + l + "! threadL");
}
}
当 threadL 运行时,我想从 运行 中断 threadR,因此导致
System.out.println("Main thread interrupted.");
将从 threadR 打印
我在代码中突出显示了应该发生中断的位置
greetings.interrupt(); //interrupt greetings thread here <-------
为什么中断不能正常工作?
您需要打断 greetingsFromFred
。这是正在启动的线程,实际上是 运行。线程 greetings
永远不会 运行。 Is 仅作为 运行nable 传递给另一个线程。为什么还要在线程的构造函数中传递其他线程?这整个设计很可疑。我认为您对 thread/runnable 以及何时以及如何使用感到困惑。
您要中断的线程称为 greetingsFromFred
而不是 greetings
,因此只要您使 greetingsFromFred
可见,此更改就会起作用。
greetingsFromFred.interrupt(); //interrupt greetings thread here <-------
您的程序有点像线程对象的迷宫。让我们尝试从 main
.
在 main
中,您创建了一个类型为 Threads
的对象 t
。这意味着它是 Thread
和 Runnable
.
然后,对于您的每个字符串,您 运行 t
的 threadL
和 threadR
将该字符串作为参数。
在 main
的任何地方你都没有真正开始 t
,甚至 运行 它的 run
方法直接开始。
然后,在 threadR
中创建一个新的空 Thread
并将其分配给 greetings
。
然后您使用那个新的空 Thread
(问候)作为一个 Runnable
,它被传递给一个新的 Thread
对象,该对象的 run
方法也被覆盖。这是徒劳的。在任何情况下,greetingsFromFred
都会启动,并且会 运行 循环。但是 greetings
仍然包含与 greetingsFromFred
线程无关的空 Thread
。
在 threadL
中,您创建了另一个 Thread
,将当前的 Thread
(即 main
中的 t
)作为其 运行启用。然后你终于开始了。它将尝试中断 greetings
中的线程,但正如我们所说,这是一个从未启动的非活动空线程。
你的结构应该没有那么复杂。尽可能只使用 Runnable
对象来处理应该执行的事情。因此,Threads
本身应该是 Runnable
,而 threadR
应该固定为这样的东西:
public void threadR(String f) {
Runnable greetingsFromFred = new Runnable() {
@Override
public void run() {
for (int i = 1; i < 5; i++) {
try {
System.out.println("Main Thread: " + i + " " + f);
Thread.sleep(5000);
} catch (InterruptedException ex) {
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread exiting.");
}
}
};
greetings = new Thread(greetingsFromFred, f);
greetings.start();
}
这样,greetings
将是一个 Thread
,它将 运行 您在 Runnable
[=70= 中创建的 循环],还有名字 f
.
但是请注意:由于您实际上并没有在 catch
子句中停止循环,因此当线程被中断时您不会退出循环,并且您可能会得到 "Exiting" 消息打印不止一次,具体取决于中断何时进入循环。
正如其他人所指出的,这里有几个问题。
不必要的继承
您的 class Threads
有一个 run()
方法可以中断分配给 greetings
的任何线程,但 R
和 L
都不会继承 run()
方法。 R
扩展了 Thread
并覆盖了 run()
,而 L
只是扩展了 Thread
而没有覆盖。这种结构没有意义。
鉴于此,run()
方法似乎应该被 L
覆盖。
如果您做到了,那么 Threads
就没有必要覆盖 Thread
或实施 Runnable
。您可以完全取消它并让 R
和 L
各自覆盖 Thread
(使用 Thread(String)
构造函数)。
public class Threads {
...
public List<Thread> threadL(String l) {
greetings1 = new Thread(l) {
@Override
public void run() {
greetings.interrupt(); // interrupt greetings thread here <-------
System.out.println("Greetings from " + l + "! threadL");
}
...
}
}
所以现在 threadR()
创建并启动一个将被中断的新 Thread
,并且 threadL()
创建并启动一个将中断的新 Thread
,并且 Threads
仅存在于 main()
方法和 threadR()
和 threadL()
中的线程 creation/starting。清洁工
中断错误的线程
正如其他人所指出的,您有这个不必要的变量 greetingsFromFred
,这只会使事情复杂化。你为 greetingsFromFred
所做的一切你应该为 greetings
做(构造函数除外)。
public void threadR(String f) {
greetings = new Thread(f) {
@Override
public void run() {
...
}
greetings.start();
}
greetings
上的竞争条件
这是最微妙的部分,IMO。您正在使用静态变量 greetings
来保存当前 Thread
对象 R
,当前 L
应该中断该对象。但是这两个线程之间没有协调。没有理由相信每个 L
都会在 greetings
中看到一个独特的 R
。这是一件非常糟糕的事情。你需要做的是让 greetings
volatile
,然后让 R
等到 greetings
为空再赋值,让 L
等到 greetings
] 在中断之前不为空。
private volatile Thread greetings;
...
public void threadR(String f) {
while (greetings != null);
greetings = new Thread(f) {
...
public void threadL(String l) {
Thread greetings1 = new Thread(l) {
@Override
public void run() {
while (greetings == null);
...
注意 while(...);
的用法。尾随的分号是一个空语句。这个结构只是说继续检查条件直到它为假,然后继续。由于 R
直到它有一个 null
然后设置它才会继续,并且 L
永远不会继续直到它有一个 non-null
然后将其置空,它们将始终保持同步.