经常有人会问:如何停止一个线程?线程类Thread
的join
、sleep
、interrupt
等方法的具体使用场景是什么?本文就线程的join
和sleep
方法来看一下具体场景下的表现。
join
先看下源码
/**
* Waits for this thread to die.
*
* An invocation of this method behaves in exactly the same
* way as the invocation
*
*
* {@linkplain #join(long) join}{@code (0)}
*
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
源码中提示:Waits for this thread to die.顾名思义,等待线程死亡。
而且该方法会抛出中断异常
那么具体会产生什么效果呢?我们来验证一下。
以下代码是在主线程循环体内部调用其他线程的join
方法,其他线程单纯的打印执行结果
/**
* 程序入口,主线程
*/
public static void main(String[] args) {
Thread thread = new Thread(new PrintTask("Thread"));
thread.start();
for (int i = 0; i < 1000; i++) {
if (i == 500) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("程序结束");
}
/**
* 子线程任务
*/
private static class PrintTask implements Runnable {
private String name;
public PrintTask(String name) {
this.name = name;
}
@Override public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(name + " ------> " + i);
}
System.out.println("-----thread finished. -----");
}
}
执行以上main
方法,输出为:
Thread ------> 995
Thread ------> 996
Thread ------> 997
Thread ------> 998
Thread ------> 999
-----thread finished. -----
程序结束
可以看出,主线程中调用其他线程的join
后,必须等待其他线程的结束,才可以继续执行。
也就是说:
自身线程内,他线程join。自身需要等待他线程结束后才会获得执行,影响自身
稍微改动一下,将主线程的join
操作移动到其他线程执行体内。
/**
* 程序入口,主线程
*/
public static void main(String[] args) {
Thread thread = new Thread(new PrintTask("Thread"));
thread.start();
for (int i = 0; i < 1000; i++) {
// 让主线程不要太快结束
Math.random();
}
System.out.println("程序结束");
}
private static class PrintTask implements Runnable {
private String name;
public PrintTask(String name) {
this.name = name;
}
@Override public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(name + " ------> " + i);
if (i == 10) {
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("-----thread finished. -----");
}
}
执行以上main
方法,输出为:
Thread ------> 0
Thread ------> 1
Thread ------> 2
Thread ------> 3
Thread ------> 4
Thread ------> 5
Thread ------> 6
Thread ------> 7
Thread ------> 8
Thread ------> 9
Thread ------> 10
程序结束
可以看出主线程的没有受到其他线程的影响,而自定义线程在join
后,线程阻塞,没有继续执行,也没有结束。也就是说:
自身线程内自身join,会造成自身等待,对其他线程没影响
sleep
先看下源码
/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
*
* @param millis
* the length of time to sleep in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public static native void sleep(long millis) throws InterruptedException;
是个静态方法,会导致当前线程阻塞一定时间。这个方法在平时可能用得比较多。这里不提供测试了。
interrupt
先看下源码说明
/**
* Interrupts this thread.
*
* If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
这里只截取了注释的一部分,从注释里可以看出,方法作用是中断线程的,但是从方法体中的注释可以看到
interrupt0(); // Just to set the interrupt flag
方法只是设置了中断标志位。在实际测试中,我们也发现,不管是在线程内部还是外部,当线程正在执行时,调用线程的interrupt
方法,都不会对线程的执行有任何影响。那么这个方法到底有什么用呢?
其实从该方法的注释中可以看到:
* If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
当线程处于阻塞状态时,调用该中断方法,会引发中断异常。下面来测试一下。
/**
* 程序入口,主线程
*/
public static void main(String[] args) {
Thread thread = new Thread(new PrintTask("Thread"));
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("Main Thread ------> " + i);
if (i == 50) {
try {
thread.join();
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("程序结束");
}
/**
* 子线程任务
*/
private static class PrintTask implements Runnable {
private String name;
public PrintTask(String name) {
this.name = name;
}
@Override public void run() {
for (int i = 0; i < 10000; i++) {
// 防止线程太快结束
Math.random();
}
System.out.println("-----thread finished. -----");
}
}
当次的测试结果为:
Main Thread ------> 49
Main Thread ------> 50
-----thread finished. -----
Main Thread ------> 51
Main Thread ------> 52
......
Main Thread ------> 98
Main Thread ------> 99
程序结束
可以看到,主线程和自定义线程都正常结束。原因也很简单,自定义线程在join
后,并没有被阻塞,调用它的中断方法后,不会造成任何影响。
下面改动一下,在自定义线程内部join
后,阻塞自定义线程,然后调用自定义线程的中断方法
/**
* 程序入口,主线程
*/
public static void main(String[] args) {
Thread thread = new Thread(new PrintTask("Thread"));
thread.start();
for (int i = 0; i < 1000; i++) {
// 让主线程不要太快结束
Math.random();
if (i == 999) {
System.out.println("主线程内中断自定义线程");
thread.interrupt();
}
}
System.out.println("程序结束");
}
/**
* 子线程任务
*/
private static class PrintTask implements Runnable {
private String name;
public PrintTask(String name) {
this.name = name;
}
@Override public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(name + " ------> " + i);
if (i == 10) {
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("-----thread finished. -----");
}
}
程序运行结果为:
Thread ------> 9
Thread ------> 10
主线程内中断自定义线程
程序结束
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at com.github.javarunable.Application$PrintTask.run(Application.java:56)
at java.lang.Thread.run(Thread.java:748)
Thread ------> 11
Thread ------> 12
可以看到在自定义线程自身join
后,处于等待状态,之后主线程调用了自定义线程的中断方法,自定义线程会收到一个中断异常。
实际测试过程中,只要自定义线程自身join,不管是在自定义线程内部还是外部调用自定义线程的中断方法,自定义线程都会收到一个中断异常。也就是说:
当线程处于阻塞或者等待状态时,调用线程的中断方法,会在线程内部产生一个中断异常
反之也是一样
当线程的中断标记为True
时,线程尝试调用自身的join
方法阻塞线程时,线程也会收到一个中断异常。
Thread ------> 997
------ interrupt state: true
Thread ------> 998
------ interrupt state: true
Thread ------> 999
尝试调用线程join方法
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at com.github.javarunable.Application$PrintTask.run(Application.java:58)
at java.lang.Thread.run(Thread.java:748)
-----thread finished. -----
再看如下代码
/**
* 程序入口,主线程
*/
public static void main(String[] args) {
Thread thread = new Thread(new PrintTask("Thread"));
thread.start();
for (int i = 0; i < 1000; i++) {
// 让主线程不要太快结束
Math.random();
if (i == 10) {
System.out.println("主线程内中断自定义线程");
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("程序结束");
}
在线程外部调用线程的join
方法,在前面知道,不会造成线程的阻塞,自然也不会产生中断异常。
在实际测试过程中,sleep
和阻塞式join
产生的效果是一致的。
如何停止一个线程?
在Thread类中有一个过时方法stop
,我们看一下他的说明
* @deprecated This method is inherently unsafe. Stopping a thread with
* Thread.stop causes it to unlock all of the monitors that it
* has locked (as a natural consequence of the unchecked
* <code>ThreadDeath</code> exception propagating up the stack). If
* any of the objects previously protected by these monitors were in
* an inconsistent state, the damaged objects become visible to
* other threads, potentially resulting in arbitrary behavior. Many
* uses of <code>stop</code> should be replaced by code that simply
* modifies some variable to indicate that the target thread should
* stop running. The target thread should check this variable
* regularly, and return from its run method in an orderly fashion
* if the variable indicates that it is to stop running. If the
* target thread waits for long periods (on a condition variable,
* for example), the <code>interrupt</code> method should be used to
* interrupt the wait.
*/
可以看出,系统不建议使用这种不安全的线程停止方法,而是建议通过变量状态的控制来结束线程。当一个线程处于长时间等待状态时,可以通过设置中断位标记,来中断这种等待。
由此,我们可以得出两种简单的结论。
线程类Thread的join、sleep、interrupt等方法的具体使用场景是什么?
根据上面的测试结果和描述,我们对于这些方法的使用应该有几个明确的要点:
关于Thread
的yield
方法,可以参考转载的一篇文章Thread.yield方法到底有什么用?