停止线程
这里我们将学习如何更好地停止一个线程。
停止线程,就是在线程正在处理指定任务的过程中,人为的不让它在继续执行下去,放弃当前的操作。停止线程看起来很简单,但是要想达到预期效果,就必须做好防范措施。
停止线程的3种方式:
1.使用退出标志,是线程正常退出,也就是执行完run方法再停止。
2.使用stop方法停止
3.使用interrupt方法中断线程
1.7.1 停止不了的线程
调用interrupt方法仅仅只是给线程一个停止标记,而不是真正的停止线程。
public class InterruptThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 50000; i++) {
System.out.println("i = " + (i+1));
}
}
}
public class InterruptThreadTest {
public static void main(String[] args){
try {
InterruptThread it = new InterruptThread();
it.start();
Thread.sleep(2000);
it.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
从运行结果上看,该线程并没有真的停止,而是让循环执行完了,这也就说明了interrupt方法不能真正的直接让线程停止,而是给线程打上了一个停止的标记。那么如何知道interrupt方法是给线程一个停止的标记呢?接下来,学习两种判断线程停止状态的方法来验证这个结论。
1.7.2判断线程是否是停止状态
判断线程停止状态有2种方法:
1.this.interrupted():测试当前线程是否已经中断
2.this.isInterrupted():测试线程是否已经中断
首先,看看interrupted方法:
public class InterruptedMethod extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
System.out.println("i = " + (i + 1));
}
}
}
public class InterruptedMethodTest {
public static void main(String[] args) {
try {
InterruptedMethod im = new InterruptedMethod();
im.start();
im.sleep(1000);
im.interrupt();
System.out.println("是否停止1? = "+ im.interrupted());
System.out.println("是否停止2? = "+ im.interrupted());//其实这里是静态方法
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end!");
}
}
从运行结果可以看到,线程thread并未停止,那么到底判断的是谁的停止状态呢?从代码中看,im.interrupt语句是让im这个线程对象有了一个停止的标记。而interrupted方法是判断当前线程是否有停止标记,当前线程其实是main线程,没有给main线程停止标记,所以两次判断都是false。
public class Run2{
public static void main(String[] args){
Thread.currentThread().interrupt();
System.out.println("是否停止1? = "+Thread.interrupted());
System.out.println("是否停止2? = "+Thread.interrupted());
System.out.println("end!");
}
}
从这次的运行结果分析,为什么一个是true,一个是false呢?Thread.currentThread()返回的是当前线程,它再调用interrupt方法,是给当前线程main一个停止标记。判断方法interrupted判断当前线程是否停止,返回true,表示main线程已经有了停止的标记,第二次为什么打印false呢?是因为interrupted方法会清除这个状态标记。
注意事项:interrupted方法判断当前线程是否停止,具有清除状态的功能。
再来看看,isInterrupted方法:
public class IsInterruptedMethodTest {
public static void main(String[] args) {
try {
InterruptedMethod im = new InterruptedMethod();
im.start();
Thread.sleep(2000);
im.interrupt();
System.out.println("是否停止1? = " + im.isInterrupted());//
System.out.println("是否停止2? = " + im.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end");
}
}
从运行结果上看到打印了2个true,也就是说isInterrupted方法没有清除状态的功能。并且它作用的是调用它的线程对象是否有停止标记,而不是判断当前线程是否有停止标记。
总结:
1.interrupt方法时给线程一个停止标记,而不能真的停止线程
2.interrupted方法是判断当前线程是否有停止标记,并且具有清除状态的功能
3.isInterrupted方法是判断线程对象是否有停止标记,没有清除状态的功能。
1.7.2能停止的线程----异常法
有了前面的学习,我们来使用一下上面的方法:
public class StopThreadUseException extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
if(this.interrupted()){
System.out.println("已经是停止状态了,我要退出了");
break;//这里的break只是跳出当前for循环,如果for循环后还有语句就继续执行,证明interrupt方法只是标记了线程的状态
}
System.out.println("i = " + (i+1));
}
//如果for循环下面还有语句,还会继续执行
System.out.println("我被输出,如果此代码是for又继续执行,线程并未停止!");
}
}
public class StopThreadUseExceptionTest {
public static void main(String[] args) {
try {
StopThreadUseException ste = new StopThreadUseException();
ste.start();
Thread.sleep(1000);
ste.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
从运行结果看,这里的break只是跳出了for循环,而线程没有停止。那么怎么才能是线程真的停止呢?有一种方式就是异常法,抛出一个异常,让线程真的停止。
public class StopThreadUseException2 extends Thread{
@Override
public void run() {
super.run();
try{
for (int i = 0; i < 500000; i++) {
if(this.interrupted()){
System.out.println("已经是停止状态了,我要退出了!");
throw new InterruptedException();
}
System.out.println("i = " + (i+1));
}
System.out.println("我在for下面");
}catch (InterruptedException e ){
System.out.println("进入StopThreadUseException2.java类run方法中的catch了");
e.printStackTrace();
}
}
}
public class StopThreadUseException2Test {
public static void main(String[] args) {
try {
StopThreadUseException2 stue = new StopThreadUseException2();
stue.start();
Thread.sleep(2000);
stue.interrupt();
}catch (InterruptedException e ){
System.out.println("main catch");
e.printStackTrace();
}
}
}
这里我们看到,线程才是真正的停止了。所以当我们要停止线程的时候,可以使用异常法。
1.7.4在沉睡中停止
如果在sleep状态下停止线程,会是什么效果呢?
public class StopInSleep extends Thread {
@Override
public void run() {
super.run();
try{
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中停止,进入catch"+this.isInterrupted());
e.printStackTrace();
}
}
}
public class StopInSleepTest {
public static void main(String[] args) {
try {
StopInSleep sis = new StopInSleep();
sis.start();
Thread.sleep(100);
sis.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
在sleep状态下,停止线程会进入run方法的catch中,捕捉异常;并且会清除状态标识。上面是先使线程进入睡眠状态,再用interrupt停止。接下来看看先用interrupt停止,再sleep。
public class StopInSleep2 extends Thread{
@Override
public void run() {
super.run();
try{
for (int i = 0; i < 100000; i++) {
System.out.println( "i = " + (i + 1));
}
System.out.println("run begin");
Thread.sleep(200000);
}catch (InterruptedException e){
System.out.println("先停止,遇到了sleep!进入catch");
}
}
}
public class StopInSleep2Test {
public static void main(String[] args) {
StopInSleep2 sis2 = new StopInSleep2();
sis2.start();
sis2.interrupt();
System.out.println("end");
}
}
1.7.5能停止的线程----暴力停止
public class Stop extends Thread {
private int i = 0;
@Override
public void run() {
try{
while(true){
i++;
System.out.println("i = " + i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
/*
* stop方法停止线程是暴力停止,不建议使用
* 并且stop方法时已过时的方法
* */
public class StopTest {
public static void main(String[] args) {
try{
Stop stop = new Stop();
stop.start();
Thread.sleep(8000);
stop.stop();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
根据代码分析,i在while循环中应该无限循环下去,但是让main线程暂停8秒后,main线程调用了stop()方法,暴力停止了线程,所以i的打印停止。stop方法可以停止线程。
注意事项:stop方法其实会抛出java.lang.ThreadDeath异常,但是在一般情况下,此异常不需要显示的捕捉。
1.7.7释放锁的不良后果
public class SynchronizedObject {
private String username = "a";
private String password = "aa";
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
synchronized public void printString(String username,String password){
try{
this.username = username;
Thread.sleep(1000000);
this.password = password;
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class MyThread extends Thread {
private SynchronizedObject object;
public MyThread(SynchronizedObject object){
super();
this.object = object;
}
@Override
public void run() {
object.printString("b","bb");
}
}
public class MyThreadTest {
public static void main(String[] args) {
try{
SynchronizedObject object = new SynchronizedObject();
MyThread mt = new MyThread(object);
mt.start();
Thread.sleep(500);
mt.stop();
System.out.println(object.getUsername() + " " + object.getPassword());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
从运行结果上看,打印的值不是我们预期的效果,是什么原因呢?
是因为调用了stop方法停止线程,会释放锁,导致多个线程在操作数据,出现了错误的情况。
所以还是不建议使用stop方法停止异常。
1.7.8 使用return停止线程
将方法interrupt()和return结合也能实现停止线程的效果:
public class MyThread extends Thread {
@Override
public void run() {
while(true){
if(this.isInterrupted()){
System.out.println("停止了");
return;
}
System.out.println("timer = "+System.currentTimeMillis());
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
mt.start();
Thread.sleep(2000);
mt.interrupt();
}
}
我们知道,return也可以马上结束当前函数,所以return和interrupt方法结合也可以停止线程。
总结:停止线程的三种方式:
1.interrupt方法并不能真的停止线程,而是给线程一个停止标记。
2.stop方法已经是过时的方法,不建议使用。
3.异常法,抛出一个异常,让线程真正的停止。
4.使用return和interrupt方法结合也可以停止线程。
但是在今后的使用中,还是建议使用抛异常法来停止线程,因为可以在catch中处理异常信息。