Appearance
线程的正确停止方式
内容来源:本篇为学习整理,转载自网络技术博客(版权归原作者所有):
看在前面
停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作。停止一个线程可以用Thread.stop()方法,但不建议用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是已被废弃的方法。
在java中有以下3种方法可以终止正在运行的线程:
使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
使用stop方法强行终止,但是不推荐这个方法。因为stop和suspend及resume一样都是过期作废的方法。
使用interrupt方法中断线程。
一、停止不了的线程
interrupt()方法的使用效果并不像for+break语句那样,立即停止循环。调用interrupt方法是在当前线程中打了一个停止标志,并不是真的停止线程。
java
public class InterruptTest {
public static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}输出结果:
java
...
i=499994
i=499995
i=499996
i=499997
i=499998
i=499999
i=500000其中interrupt()方法源码如下:
java
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> 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}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
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();
}二、判断线程是否停止状态
Thread.java类中提供了两种方法:
this.interrupted(): 测试当前线程是否已经中断;
this.isInterrupted(): 测试线程是否已经中断;
interrupted()是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态。interrupted()源码如下:
java
/**
* Tests whether the current thread has been interrupted. The
* <i>interrupted status</i> of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if the current thread has been interrupted;
* <code>false</code> otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的isInterrupted(),不会重置当前线程的中断状态。isInterrupted()源码如下:
java
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if this thread has been interrupted;
* <code>false</code> otherwise.
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}这两个方法的区别如下。
我们先来看看this.interrupted()方法的解释:测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程。
java
public class InterruptTest {
public static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
i++;
// System.out.println("i=" + (i + 1));
}
}
}
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
System.out.println("stop 1??" + thread.interrupted());
System.out.println("stop 2??" + thread.interrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}运行结果:
java
stop 1??false
stop 2??false类Run.java中虽然是在thread对象上调用以下代码:thread.interrupt(), 后面又使用
java
System.out.println("stop 1??" + thread.interrupted());
System.out.println("stop 2??" + thread.interrupted());来判断thread对象所代表的线程是否停止。但从控制台打印的结果来看,线程并未停止。这也证明了interrupted()方法的解释:测试当前线程是否已经中断。这个当前线程是main,它从未中断过,所以打印的结果是两个false.
让main线程产生中断效果的写法如下:
java
public static void main(String[] args){
Thread.currentThread().interrupt();
System.out.println("stop 1??" + Thread.interrupted());
System.out.println("stop 2??" + Thread.interrupted());
System.out.println("End");
}运行效果为:
java
stop 1??true
stop 2??false
End方法interrupted()的确判断出当前线程是否是停止状态。但第2个布尔值是false的原因如下。
官方帮助文档中对interrupted方法的解释:测试当前线程是否已经中断。线程的中断状态由该方法清除。 换句话说,如果连续两次调用该方法,则第二次调用返回false。
下面来看inInterrupted()方法。
java
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
thread.interrupt();
System.out.println("stop 1??" + thread.isInterrupted());
System.out.println("stop 2??" + thread.isInterrupted());
}运行结果:
java
stop 1??true
stop 2??true其中this.isInterrupted(): 测试线程是否已经中断;上面调用了interrupt()方法,所以输出两个true。
三、能停止的线程--异常法
有了前面学习过的知识点,就可以在线程中用for语句来判断线程是否是停止状态。如果是停止状态,则后面的代码不再运行即可:
java
public class InterruptTest {
public static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
if (this.isInterrupted()) {
System.out.println("线程已经终止,for循环不再执行");
break;
}
System.out.println("i = " + (i + 1));
}
}
}
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}运行结果:
java
...
i=202053
i=202054
i=202055
i=202056
线程已经终止, for循环不再执行上面的示例虽然停止了线程,但如果for语句下面还有语句,还是会继续运行的。看下面的例子:
java
public static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
if (this.isInterrupted()) {
System.out.println("线程已经终止,for循环不再执行");
break;
}
System.out.println("i = " + (i + 1));
}
System.out.println("这是for循环外面的语句,也会被执行");
}
}使用Run.java执行的结果是:
java
···
i = 411434
i = 411435
i = 411436
i = 411437
i = 411438
i = 411439
i = 411440
线程已经终止,for循环不再执行
这是for循环外面的语句,也会被执行解决语句继续运行问题的更新代码如下:
java
public static class MyThread extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 500000; i++) {
if (this.isInterrupted()) {
System.out.println("线程已经终止,for循环不再执行");
throw new InterruptedException();
}
System.out.println("i = " + (i + 1));
}
System.out.println("这是for循环外面的语句,也会被执行");
} catch (InterruptedException e) {
System.out.println("进入MyThrad.java类中的catch了......");
e.printStackTrace();
}
}
}使用Run.java运行的结果如下:
java
···
i = 402032
i = 402033
i = 402034
i = 402035
线程已经终止,for循环不再执行
进入MyThrad.java类中的catch了......
java.lang.InterruptedException
at com.demotransfer.java.basic.InterruptTest$MyThread.run(InterruptTest.java:12)四、在沉睡中停止
线程在sleep()状态下停止线程的效果如下。
java
public class MyThread extends Thread {
public void run(){
super.run();
try {
System.out.println("线程开始。。。");
Thread.sleep(200000);
System.out.println("线程结束。");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止, 进入catch, 调用isInterrupted()方法的结果是:" + this.isInterrupted());
e.printStackTrace();
}
}
}使用Run.java运行的结果是:
java
线程开始。。。
在沉睡中被停止, 进入catch, 调用isInterrupted()方法的结果是:false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at thread.MyThread.run(MyThread.java:12)从打印的结果来看, 如果在sleep状态下停止某一线程,会进入catch语句。并且清除停止状态值,使之变为false。
前一个实验是先sleep然后再用interrupt()停止,与之相反的操作在学习过程中也要注意:
java
public class MyThread extends Thread {
public void run(){
super.run();
try {
System.out.println("线程开始。。。");
for(int i=0; i<10000; i++){
System.out.println("i=" + i);
}
Thread.sleep(200000);
System.out.println("线程结束。");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到sleep,进入catch异常");
e.printStackTrace();
}
}
}
public class Run {
public static void main(String args[]){
Thread thread = new MyThread();
thread.start();
thread.interrupt();
}
}运行结果:
java
i=9998
i=9999
先停止,再遇到sleep,进入catch异常
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at thread.MyThread.run(MyThread.java:15)