线程如何结束
为什么不用stop()方法
一般来说,当一个线程的方法体执行完,这个方法就会结束了。我们一般不直接使用stop方法来终止线程,这可能会导致业务逻辑不完整或者破坏原子性的操作,一段代码可能用来输出某些重要的信息,在方法的执行中使用stop方法终止线程,会使得输出的信息破坏或者不完整;在原子操作比如重入锁的使用时,当一个线程对象多次获得锁会使得计数加一,接下来需要以相同的次数释放锁直到计数减到0,别的线程才能获得锁资源,这种操作是原子性的,因为对线程来说,他们拿到锁和最终释放锁时计数都是为0的,因此这是一种原子性操作,假设在一个对象使用锁的期间,使用stop方法强行终止线程,这会导致锁的计数不为0,即破坏了原子性操作。综合来说,我们如果要正确地终止一个线程,不应该使用stop()这种过时的方法。
比较常用的是以下两种方法:
1、使用while循环轮询标志位,为false则退出循环,结束方法体,终止线程。
2、使用interrupt()方法在线程阻塞状态下结束线程。
方法1:使用标志位
这种方法是我尝试终止线程时最初采用的方法,使用起来很方便,原理也很简单,只要在while循环里持续判断一个Boolean变量的值就可以了,一旦满足离开循环的条件,那么就可以离开线程的方法体,结束线程。
public class Test {
boolean flag=true;
public static void main(String[] args){
Test test = new Test();
test.demo();
}
public void demo(){
Thread t1 = new Thread(){
public void run() //重写run方法
{
while(flag==true)
{
System.out.println("线程在执行");
}
System.out.println("线程结束了。。。。。。。。");
}
};
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
flag=false;
}
}
2、运行结果:
线程在执行
线程在执行
线程在执行
线程在执行
线程在执行
线程在执行
线程在执行
线程在执行
线程结束了。。。。。。。。
3、分析:我们先创建一个线程并运行它,在while循环里不断输出“线程在执行”,然后我们修改标志位为false,离开循环并结束了线程。
方法2:使用Interrupt方法(中断)
使用中断的方法可以正确结束一个线程。
使用到的API介绍:
1、interrupt()方法:对某个线程对象调用这个方法就可以把它的中断标志位置为true,但它不能立刻结束一个线程。
2、currentThread()方法:静态方法,用线程类调用获得当前线程对象的引用。
4、sleep():使当前线程睡眠,释放资源但是不释放锁。
5、join():等待线程终止,停住调用这个方法的当前线程,等待指定的线程运行结束。减少程序运行的不确定性。
https://www.cnblogs.com/qlqwjy/p/10120457.html
6、start():启动一个线程。
7、interrupted()方法:是静态方法,测试当前线程是否被中断,同时清除中断标志。也就是说,在中断标志置位后第一次调用返回true,第二次调用返回false。
8、isinterrupt()方法:这个方法由对象调用,测试线程是否已经被中断,仅仅返回标志的状态,不会对标志有任何影响。
轮询中断标志退出线程:
思路:
main方法中新建并启动线程,然后延时一段时间,置位子线程的中断标志。子线程检测到中断标志被置位,则离开循环,结束线程。
代码:
public class Test {
public static void main(String[] args){
Test test = new Test();
test.demo();
}
public void demo(){
Thread tt = new Thread()
{
public void run() {
int add=0; //记录循环次数
System.out.println("线程开始");
while(true)
{
if(this.isInterrupted())
break;
add++;
System.out.println("执行次数:"+add);
}
System.out.println("线程结束");
}
};
tt.start();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tt.interrupt();
}
}
运行结果:
子线程主动抛异常,并捕获,结束线程(解决上述方案在break后还会执行后续代码的问题)
思路:
子线程try,catch代码块,准备捕获中断异常。子线程在检测到中断标志后,主动throw一个异常,被捕获并执行捕获处理程序,结束线程。
public class Test {
public static void main(String[] args){
Test test = new Test();
test.demo();
}
public void demo(){
Thread tt = new Thread()
{
public void run() {
int add=0; //记录循环次数
System.out.println("线程开始");
try{
while(true){
if(this.isInterrupted()){
System.out.println("检测到中断标志为true,抛一个异常");
throw new InterruptedException();
}
add++;
System.out.println("执行次数:"+add);
}
}catch(InterruptedException e){
System.out.println("接收到了抛出的异常,直接结束线程");
e.printStackTrace();
}
}
};
tt.start();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tt.interrupt();
}
}
运行结果:
详解
使用interrupt()方法中断当前线程
1.线程处于阻塞状态,如使用了sleep,同步锁的wait,socket中的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptException异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后break跳出循环状态,从而让我们有机会结束这个线程的执行。通常很多人认为只要调用interrupt方法线程就会结束,实际上是错的, 一定要先捕获InterruptedException异常之后通过break来跳出循环,才能正常结束run方法。
public class ThreadSafe extends Thread {
public void run() {
while (true){
try{
Thread.sleep(5*1000);//阻塞5妙
}catch(InterruptedException e){
e.printStackTrace();
break;//捕获到异常之后,执行break跳出循环。
}
}
}
}
2.线程未处于阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。
代码示例:
public class ThreadSafe extends Thread {
public void run() {
while (!isInterrupted()){
//do something, but no throw InterruptedException
}
}
}
为什么要区分进入阻塞状态和和非阻塞状态两种情况了,是因为当阻塞状态时,如果有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完之后会复位中断状态为false,所以异常抛出之后通过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,
因此在线程未进入阻塞的代码段时是可以通过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要通过捕获异常来退出循环。因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑:
public class ThreadSafe extends Thread {
public void run() {
while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出
try{
Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出
}catch(InterruptedException e){
e.printStackTrace();
break;//捕获到异常之后,执行break跳出循环。
}
}
}
}
在线程睡眠时置位中断标志:
思路:
新建、运行线程并让其睡眠,然后在main方法中把中断标志置为true,子线程在睡眠中会不断查询这个标志,检测到标志为true则抛出一个中断异常,结束线程。若把sleep换为join,效果相同(都是陷入阻塞状态)。
public class Test {
public static void main(String[] args){
Test test = new Test();
test.demo();
}
public void demo(){
Thread tt = new Thread(){
public void run() {
System.out.println("线程开始");
try{
sleep(3000);
}catch(InterruptedException e){
System.out.println("捕获到了一个中断异常");
System.out.println("线程结束");
e.printStackTrace();
}
}
};
tt.start();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tt.interrupt();
}
}