3种方法
在Java 中有 3种方法可以使正在运行的线程终 止运行
1 )使用退出标志使线程正常退出。
2 )使用 stop()方法强行终止线程,但是这个 方法不推荐使用,因为 stop() 和 suspend() 、
resume()一样,都是作废过期的方法,使用它们可 能发生不可预料的结果。
3 )使用 interrupt() 方法中断线程。
调用interrupt()方法来停止线程
但interrupt() 方法的使用效果并不像 for+break语句那样,马上就停止循环。调用 interrupt()方法仅仅是在当前线程中做了一个停止 的标记,并不是真正停止线程。
文件MyThread.java代码
package test1_11;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 10000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
运行类Run.java代码
package test1_11;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(20);
thread.interrupt();
System.out.println("zzzzzzzzzzzzzzzzzzzzzzz");
}
}
运行结果
在 4357 行处输出了 zzzzzzzz ,说明 sleep 时间为20ms 时, for 语句执行了 4357 次循环
从运行结果来看,调用 interrupt()方法并没有将线程停 止,那如何停止线程呢?
判断线程是否为停止状态
Thread.java 类提供了两个判断方法
1 ) public static boolean interrupted():测试 currentThread() 是否已经中断。
/** * 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);
}
2)public boolean this.isInterrupted():测试this关键 字所在类的对象是否已经中断。
/** * 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);
}
/**
* 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);
这两个方法可以判断线程是否是停止状态,那么这 两个方法有什么区别呢?
先来看看方法this.interrupted() 方法的解释
测试当前线程是否已经中断,当前线程是 指运行 this.interrupted() 方法的线程。
类MyThread.java 代码
package test1_11.interrupted;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 10000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
类Run.java代码
package test1_11.interrupted;
public class Run {
public static void main(String[] args) throws InterruptedException {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(20);
thread.interrupt(); //Thread.currentThread().interrupt();
System.out.println("是否停止1?=" + thread.interrupted());
System.out.println("是否停止2?=" + thread.interrupted());
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
程序运行结果
Run.java 类虽然通过在 thread 对象上调用代码:
thread.interrupt();
来停止 thread对象所代表的线程,在后面又使用代码:
System.out.println(" 是否停止
1
?
="+thread.interrupted());
System.out.println(" 是否停止
2
?
="+thread.interrupted());
来判断thread对象所代表的线程是否停止,但从控 制台输出的结果来看,线程并未停止,这也证明了interrupted()方法的解释:测试当前线程是否已经中断。这个“当前线程”是main,从未中断过,所以输出的结果是2个false
注意
测试代码中使用 thread.interrupted()来判断 currentThread()是否被中断,也可以使用代码 Thread.interrupted() 判断,因为在 Thread.java类中调用静 态 static 方法时,大多数是针对 currentThread()线程进行 操作的。
如何使main线程有中断效果
创建Run2.java
package test1_11.interrupted;
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!");
}
}
运行结果
是否停止1?=true
是否停止2?=false
end!
从上述结果来看, interrupted()方法的确判断了当 前线程是否是停止状态,但为什么第二个布尔值是false 呢?查看一下 interrupted()方法在官方帮助文档中的解释:
测试当前线程是否已经中断。线程的中断状态由该 方法清除。换句话说,如果连续两次调用该方法,则第 二次调用将返回false(在第一次调用已清除了其中断状 态之后,且第二次调用检验完中断状态前,当前线程再 次中断的情况除外)。
文档已经解释得很详细, interrupted()方法具有清 除状态的功能,所以第二次调用 interrupted()方法返回 的值是 false 。
isInterrupted()方法的声明如下
public boolean isInterrupted()
从声明中可以看出isInterrupted()方法不是static()方 法,作用于调用这个方法的对象
创建MyThread.java类
package test1_11.interrupted;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 1000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
创建Run3.java类
package test1_11.isInterrupted;
import test1_11.interrupted.MyThread;
public class Run3 {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2);
thread.interrupt();
System.out.println("是否停止1?=" + thread.isInterrupted());
System.out.println("是否停止2?=" + thread.isInterrupted());
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
运行结果
从结果中可以看到,isInterrupted()方法并未清除状 态标志,不具有此功能,所以输出两个 true 。
综上所述,这两个方法的区别如下
1)this.interrupted():测试当前线程是否已经是中 断状态,执行后具有清除状态标志值为false的功能。
2)this.isInterrupted():测试线程Thread对象是否已 经是中断状态,不清除状态标志
能停止的线程——异常法
根据前面所学知识,只需要通过线程的for语句来判断 一下线程是否处于停止状态即可判断后面的代码是否可运 行,如果线程处于停止状态,则后面的代码不再运行
类MyThread.java代码
package test1_11.test1;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 1000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
}
}
类Run.java代码
package test1_11.test1;
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
运行结果
但如果for语句下面还有语句,那么程序还是会继续运行。
类MyThread.java代码
package test1_11.test2;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 1000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("我被输出,如果此代码是for又继续运行,线程并未停止!");
}
}
文件Run.java代码
package test1_11.test2;
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
运行结果
如何解决语句继续运行的问题呢?
看一下更新后的代码
类MyThread.java代码
package test1_11.test3;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 1000; 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("进MyThread.java类run方法中的catch了!");
e.printStackTrace();
}
}
}
类Run.java代码
package test1_11.test3;
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
运行结果
由程序运行结果可以看出,线程终于被正确停止了。 这种方式就是前面 介绍的第三种停止线程的方法 ——使用 interrupt() 方法中断线程
在sleep状态下停止线程
类MyThread.java代码
package test1_11.test4;
public class MyThread 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();
}
}
}
文件Run.java代码
package test1_11.test4;
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
运行效果
从运行结果来看,如果线程在 sleep状态下停止,则该线程 会进入 catch 语句,并且清除停止状态值,变成 false。 标例是先调用 sleep() 方法,再调用interrupt()方法停止线 程,
先调用 interrupt()方法,再调用sleep()方法
还有一个反操作在学习线程时也要注意,即先调用 interrupt() 方法,再调用 sleep()方法,这种情况下也会出现异 常。
类MyThread.java代码
package test1_11.test5;
public class MyThread 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);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到了sleep!进入catch!");
e.printStackTrace();
}
}
}
类Run.java代码
package test1_11.test5;
public class Run {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
thread.interrupt();
System.out.println("end!");
}
}
运行结果
不管其调用的顺序,只要 interrupt() 和 sleep()方法碰到一起 就会出现异常:
1 )在 sleep 状态执行 interrupt() 方法会出现异常;
2 )调用 interrupt()方法给线程打了中断的标记,再执行 sleep() 方法也会出现异常。
用stop()方法暴力停止线程
使用 stop()方法可以强行停止线程,即暴力停 止线程。
文件 MyThread.java代码
package test1_11.test6;
public class MyThread extends Thread {
private int i = 0;
@Override
public void run() {
try {
while (true) {
i++;
System.out.println("i=" + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
文件Run.java代码
package test1_11.test6;
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.stop();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果
由运行结果可以看出,线程被暴力停止了, 这种方式就是前面 介绍的第二种停止线程的方法
—— 使用stop()方法强行终止线程。
stop()方法呈删除线程状态,是不再被采用的 方法,原因是 stop() 方法容易造成业务处理的不确
定性;
例如, A 线程执行如下业务:
增加数据1
增加数据2
增加数据3
增加数据4
这时在任意时机对A线程调用stop()方法,A线程并不能确定在哪里被停止了,造成数据增加得
不完整。
stop()方法与java.lang.ThreadDeath 异常
调用 stop()方法时会抛出java.lang.ThreadDeath异常,但在通常情况下,此异常不需要显式地捕捉
文件MyThread.java代 码
package test1_11.test7;
public class MyThread extends Thread {
@Override
public void run() {
try {
this.stop();
} catch (ThreadDeath e) {
System.out.println("进入了catch()方法!");
e.printStackTrace();
}
}
}
文件Run.java代码
package test1_11.test7;
public class Run {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
运行结果
stop()方法已经是作废的方法,因为如果暴力 性地强制让线程停止,则一些清理性的工作可能 得不到完成,或者数据添加不完整。
使用stop()释放锁给数据造成不一致的结果
对锁定的对象进行 “ 解锁”,会导致数据得不到同步的处 理,进而出现数据不一致的问题。本节将会讲解使用stop()释放 锁给数据造成不一致性的结果,如果出现这样的情况,则程序 处理的数据完全有可能遭到破坏,最终导致程序执行的流程是 错误的,在此一定要注意。
文件MyService.java代码
package test1_11.test8;
public class MyService {
private String username = "a";
private String password = "aa";
synchronized public String getUsername() {
return username;
}
synchronized public String getPassword() {
return password;
}
synchronized public void printString(String username, String password) {
try {
this.username = username;
Thread.sleep(100000000);
this.password = password;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
调用业务方法printString()的线程代码
package test1_11.test8;
public class MyThreadA extends Thread {
private MyService object;
public MyThreadA(MyService object) {
super();
this.object = object;
}
@Override
public void run() {
object.printString("b", "bb");
}
}
输出username和password的线程代码
package test1_11.test8;
public class MyThreadB extends Thread {
private MyService object;
public MyThreadB(MyService object) {
super();
this.object = object;
}
@Override
public void run() {
System.out.println("username=" + object.getUsername());
System.out.println("password=" + object.getPassword());
}
}
文件Run.java代码
package test1_11.test8;
public class Run {
public static void main(String[] args) {
try {
MyService object = new MyService();
MyThreadA threadA = new MyThreadA(object);
threadA.start();
Thread.sleep(100);
MyThreadB threadB = new MyThreadB(object);
threadB.start();
Thread.sleep(3000);
threadA.stop();
System.out.println("stop()执行后,在下方开始打印username和password。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果
由于 stop() 方法已经在 JDK 中被标明是 “ 作废 / 过期 ”的方法, 显然它在功能上具有缺陷,所以不建议在程序中使用 stop()方法 停止线程。
使用“return;”语句停止线程的缺点与解决 方案
将 interrupt() 方法与 “return ; ”语句结合使用也能实现停 止线程的效果。
线程类 MyThread.java代码
package test1_11.test9;
public class MyThread extends Thread {
@Override
public void run() {
int i=0;
while (true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("i=" + i++);
}
}
}
运行类Run.java代码
package test1_11.test9;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
Thread.sleep(2);
t.interrupt();
}
}
运行结果
虽然使用 “return ; ” 较 “ 抛异常 ”法在代码结构上可以更 加方便地实现线程的停止,不过还是建议使用 “抛异常”法, 因为在catch块中可以对异常的信息进行统一的处理,
例 如,使用“return;”来设计代码:
package test1_11.test10;
public class MyThread extends Thread {
@Override
public void run() {
// insert操作
if (this.interrupted()) {
System.out.println("写入log info");
return;
}
// update操作
if (this.interrupted()) {
System.out.println("写入log info");
return;
}
// delete操作
if (this.interrupted()) {
System.out.println("写入log info");
return;
}
// select操作
if (this.interrupted()) {
System.out.println("写入log info");
return;
}
System.out.println("for for for for for");
}
}
在每个 “return ;”代码前都要搭配一个写入日志的代 码,这样会使代码出现冗余,不利于代码的阅读与扩展, 这时可以使用 “抛异常”法来简化这段代码:
package test1_11.test10;
public class MyThread2 extends Thread {
@Override
public void run() {
try {
// insert操作
if (this.interrupted()) {
throw new InterruptedException();
}
// update操作
if (this.interrupted()) {
throw new InterruptedException();
}
// delete操作
if (this.interrupted()) {
throw new InterruptedException();
}
// select操作
if (this.interrupted()) {
throw new InterruptedException();
}
System.out.println("for for for for for");
} catch (InterruptedException e) {
System.out.println("写入log info");
e.printStackTrace();
}
}
}
写入日志的功能在 catch块中被统一处理了,代码风格 更加标准。