停止线程在java语言中并不像break,return那样干脆,需要一定的技巧性。
之前认为线程的停止很简单,一个interupt()方法就完成了,事实上这是完全错误的,线程的停止API中确实提供了一个简单的方法stop(),但是在新的API中已经被标记过时了,原因就是他不够安全。
在java中提供了2中方法用来终止正在运行的线程:
1)使用退出标志,使线程正常退出,也就是run()完成后线程终止
2)使用stop强行终止线程,但是不推荐,产生不可预料的后果
3)使用interrupt终止线程
- interrupt()并不能停止线程
可能我说了,你也不信,那就来个demo验证一下
public class A extends Thread {
@Override
public void run() {
for(int j=0;j<50000;j++){
System.out.println("j="+j);
}
}
}
public static void main(String[] args) {
A a = new A();
a.start();
try {
Thread.sleep(2000);
a.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(e.toString());
}
}
看一下打印结果:
没少打印吧,这就说明interrupt()并不能停止线程,调用interrupt()仅仅是在当前线程中打了一个停止标记,并不是真的停止线程。
- 正确判定线程的运行状态
想要正确的停止线程,必须要知道线程处于生命周期的什么时期,必须要了解这几个API.
1) interrupted();
2) isInterrupted();
先看一下这2个的介绍:
这里说的很清晰了,interrupted()测试当前线程是否中断,并且清除中断状态;isInterrupted()是测试线程是否已经中断,并不影响线程的状态;
下面来个demo说明一下:
public static void main(String[] args) {
A a = new A();
a.start();
try {
Thread.sleep(2000);
a.interrupt();
System.out.println("interrupted:"+a.interrupted());
System.out.println("interrupted:"+a.interrupted());
System.out.println("is interrupted:"+a.isInterrupted());
System.out.println("is interrupted:"+a.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(e.toString());
}
}
打印结果
再来修改一下:
public static void main(String[] args) {
A a = new A();
a.start();
try {
Thread.sleep(2000);
a.interrupt();
Thread.currentThread().interrupt();
System.out.println("interrupted:"+Thread.interrupted());
System.out.println("interrupted:"+Thread.interrupted());
System.out.println("is interrupted:"+a.isInterrupted());
System.out.println("is interrupted:"+a.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(e.toString());
}
}
再看一下打印:
这个结果就印证了API上对interrupted()的说明,是测试当前线程的状态,并清除中断状态。
分析:先看第一种情况,只是执行了a.interrupt(),虽然也执行了a.interrupted(),但是还是检测的当前线程,主线程(Main)一直没有中断,所以第一次的interrupted=false;
第二种情况:Thread.currentThread.interrupt()中断了主线程,所以第一次interrupted=true,由于他还有个作用清除当前线程的中断状态,所以第二次的时候interrupted=false;
在修改一下,看下isInterrupted
public class A extends Thread {
@Override
public void run() {
for(int j=0;j<500000;j++){
System.out.println("j="+j);
if(this.isInterrupted()){
System.out.println("is interrupted:"+this.isInterrupted());
System.out.println("is interrupted:"+this.isInterrupted());
break;
}
}
}
}
public static void main(String[] args) {
A a = new A();
a.start();
try {
Thread.sleep(2000);
a.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(e.toString());
}
}
看下打印结果:
这里看到isInterrupted()并不会清除线程的终止状态,所以2次都会true,虽然状态是ture,只是跳出了循环,但是线程仍然会运行。
- 异常法停止线程
有了前面的检测线程状态的API,就可以为线程的停止,提供依据
public class A extends Thread {
@Override
public void run() {
try{
for(int j=0;j<500000;j++){
System.out.println("j="+j);
if(this.isInterrupted()){
throw new InterruptedException("thread is interrupted");
}
}
System.out.println("for 之后运行");
//do some
}catch(Exception e){
System.out.println(e.toString());
}
}
}
public static void main(String[] args) {
try {
A a = new A();
a.start();
Thread.sleep(1000);
a.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(e.toString());
}
}
看下打印结果
线程确实停止了,for语句后的也没有执行
return停止线程
将上面代码中的 throw new InterruptedException(“thread is interrupted”);改为return;也可以进行线程的终止,但是最好还是用异常法,因为catch块中的异常可以向上进行抛,使线程停止事件得以传播使用stop停止线程(不建议使用)
先看一下使用会有什么样的不良后果,
public class SynObject {
private String name="a";
private String pwd="aaa";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
synchronized public void printString(String name,String pwd){
try {
this.name = name;
Thread.sleep(100*1000);
this.pwd = pwd;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class A extends Thread {
private SynObject object;
public A (SynObject object){
this.object = object;
}
@Override
public void run() {
object.printString("b", "bbb");
}
}
public static void main(String[] args) throws Exception {
SynObject object = new SynObject();
A a = new A(object);
a.start();
Thread.sleep(500);
a.stop();
System.out.println("name:"+object.getName()+",pwd:"+object.getPwd());
}
这个逻辑很简单,object属性是有进行初始化的,printString对属性进行了重新赋值,看下结果
虽然线程是被及时停止了,但是获得的结果并不是我们预想的。stop()强行停止,可能使一些清理性的工作得不到完成,造成内存的泄漏;另外一个就是对锁定对象进行了“解锁”,导致数据得不到同步处理,出现数据不一致的问题;所以最好不要用
- b