================================================================
PS:如果你觉得文章对你有所帮助,别忘了推荐或者分享,因为有你的支持,才是我续写下篇的动力和源泉!
作者: Greta Wang
出处: http://www.cnblogs.com/greta/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
===============================================================
停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作。停止一个线程可以用Thread.stop()方法,但最好不要用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是已被废弃的方法。在java中有以下3种方法可以终止正在运行的线程:
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
- 使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
- 使用interrupt方法中断线程。
1. 停止不了的线程
interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt方法是在当前线程中打了一个停止标志,并不是真的停止线程。
public class MyThread extends Thread {
public void run(){
super.run();
for(int i=0; i<500000; i++){
System.out.println("i="+(i+1));
}
}
}
public class Run {
public static void main(String args[]){
Thread thread = new MyThread();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
...
i=499994
i=499995
i=499996
i=499997
i=499998
i=499999
i=500000
理解
在当前线程中打了一个停止标志,并不是真的停止线程
。并不是让当前线程停止执行,就是设置一个标志位,表示当前执行过中断,在线程不同状态的情况下,执行情况不一样。看下面源码,发现大部分其实都是设置中断标志位+抛出异常
的情况。例如下面的被wait()阻塞,就会then its interrupt status will be cleared and it * will receive an {@link InterruptedException}.
抛出InterruptedException
中断异常。所以就算不能阻止线程停止,但是我们可以通过抛出异常和判断标志位的方式进行操作终止。
```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();
}
2. 判断线程是否停止状态
Thread.java类中提供了两种方法:
- this.interrupted(): 测试当前线程是否已经中断;
- this.isInterrupted(): 测试线程是否已经中断;
那么这两个方法有什么图区别呢?我们先来看看this.interrupted()方法的解释:测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程。
public class MyThread extends Thread {
public void run(){
super.run();
for(int i=0; i<500000; i++){
i++;
// System.out.println("i="+(i+1));
}
}
}
public class Run {
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();
}
}
}
stop 1??false
stop 2??false
来判断thread对象所代表的线程是否停止,但从控制台打印的结果来看,线程并未停止,这也证明了interrupted()方法的解释,测试当前线程是否已经中断。这个当前线程是main,它从未中断过,所以打印的结果是两个false.
看源码
为静态方法,判断当前线程是否中断
/**
* 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);
}
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
调用isInterrupted(true)
方法传入值为true,所以会重置中断状态。
如果当前main方法进行中断,可以看出是可恢复的。除非再执行一次中断操作。
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");
}
结果:
stop 1??true
stop 2??false
End
Process finished with exit code 0
public static void main(String args[]){
Thread.currentThread().interrupt();
System.out.println("stop 1??" + Thread.interrupted());
Thread.currentThread().interrupt();
System.out.println("stop 2??" + Thread.interrupted());
System.out.println("End");
}
结果:
stop 1??true
stop 2??true
End
Process finished with exit code 0
isInterrupted()方法
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());
}
stop 1??true
stop 2??true
Process finished with exit code 0
不会进行中断状态的重置。
3. 能停止的线程–异常法
有了前面学习过的知识点,就可以在线程中用for语句来判断一下线程是否是停止状态,如果是停止状态,则后面的代码不再运行即可:
public class run {
public static void main(String args[]){
Thread thread = new MyThread();
thread.start();
try {
Thread.sleep(1000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread extends Thread {
public void run(){
super.run();
for(int i=0; i<500000; i++){
if(this.interrupted()) {
System.out.println("线程已经终止, for循环不再执行");
break;
}
System.out.println("i="+(i+1));
}
for(int u=0;u<5000;u++){
System.out.println(u);
}
System.out.println("这是for循环外面的语句,也会被执行");
}
}
i=247932
i=247933
i=247934
i=247935
i=247936
线程已经终止, for循环不再执行
0
1
2
...
4994
4995
4996
4997
4998
4999
这是for循环外面的语句,也会被执行
上面的示例虽然停止了线程,但如果for语句下面还有语句,还是会继续运行的。因为线程事实上并没有中断。如何解决语句继续运行的问题呢?看一下更新后的代码:
public class MyThread extends Thread {
public void run(){
super.run();
try {
for(int i=0; i<500000; i++){
if(this.interrupted()) {
System.out.println("线程已经终止, for循环不再执行");
throw new InterruptedException();
}
System.out.println("i="+(i+1));
}
System.out.println("这是for循环外面的语句,也会被执行");
} catch (InterruptedException e) {
System.out.println("进入MyThread.java类中的catch了。。。");
e.printStackTrace();
}
}
}
...
i=203798
i=203799
i=203800
线程已经终止, for循环不再执行
进入MyThread.java类中的catch了。。。
java.lang.InterruptedException
at thread.MyThread.run(MyThread.java:13)
4. 在沉睡中停止
在上面源码中可以看到在睡眠中中断,会抛出中断异常,所以我们直接利用中断异常进行终止操作。
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();
}
}
}
线程开始。。。
在沉睡中被停止, 进入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()停止,与之相反的操作在学习过程中也要注意:
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();
}
}
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)
可以看出中断进行时,线程并没有立即中断,直到遇到sleep操作,即抛出中断异常,终止线程。
5. 能停止的线程—暴力停止
public class MyThread extends Thread {
private int i = 0;
public void run(){
super.run();
try {
while (true){
System.out.println("i=" + i);
i++;
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String args[]) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.stop();
}
}
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
Process finished with exit code 0
调用stop()方法时会抛出java.lang.ThreadDeath异常,但是通常情况下,此异常不需要显示地捕捉。
stop()方法以及作废,因为如果强制让线程停止有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了解锁,导致数据得不到同步的处理,出现数据不一致的问题。
7. 释放锁的不良后果
使用stop()释放锁将会给数据造成不一致性的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,最终导致程序执行的流程错误,一定要特别注意:
public class SynchronizedObject {
private String name = "a";
private String password = "aa";
public synchronized void printString(String name, String password){
try {
this.name = name;
Thread.sleep(100000);
this.password = password;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class MyThread extends Thread {
private SynchronizedObject synchronizedObject;
public MyThread(SynchronizedObject synchronizedObject){
this.synchronizedObject = synchronizedObject;
}
public void run(){
synchronizedObject.printString("b", "bb");
}
}
public class Run {
public static void main(String args[]) throws InterruptedException {
SynchronizedObject synchronizedObject = new SynchronizedObject();
Thread thread = new MyThread(synchronizedObject);
thread.start();
Thread.sleep(500);
thread.stop();
System.out.println(synchronizedObject.getName() + " " + synchronizedObject.getPassword());
}
b aa
由于stop()方法以及在JDK中被标明为“过期/作废”的方法,显然它在功能上具有缺陷,所以不建议在程序张使用stop()方法。
8. 使用return停止线程
将方法interrupt()与return结合使用也能实现停止线程的效果:
public class MyThread extends Thread {
public void run(){
while (true){
if(this.isInterrupted()){
System.out.println("线程被停止了!");
return;
}
System.out.println("Time: " + System.currentTimeMillis());
}
}
}
public class Run {
public static void main(String args[]) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
Time: 1467072288503
Time: 1467072288503
Time: 1467072288503
线程被停止了!
不过还是建议使用“抛异常”的方法来实现线程的停止,因为在catch块中还可以将异常向上抛,使线程停止事件得以传播。