1. 无法停止的线程
- interrupt方法 “停止线程” ,这个方法其实是一个伪停止,并不是像break那样直接跳出去就完事了,它仅仅是给当前线程中做了一个停止的标记并不是真正停止线程。
/*
1. interrupt方法并不会中断当前正在运行的线程。
2. 只是在这个线程状态上打上中断标记。
3. interrupt会抛出InterruptedException异常。
*/
class MyThread extends Thread{
@Override
public void run() {
for(int i = 1;i <= 500;i++){
System.out.println(Thread.currentThread().getName() + " -> " + i);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(1);
myThread.interrupt();
System.out.println("zzzzzzz~");
}
}
这段代码时能够运行到结束的会输出500个i,最后输出"zzzzzzz~"。从运行结果来看interrupt方法的确无法停止线程。并不是因为循环次数太小,换大一点也是一样。
2. 判断线程是否为停止状态的两个方法
- public static boolean interrupted();测试currentThread()是否停止。首先注意这个currentThread是表示当前这条语句写在哪个线程里就是哪个线程的状态,并且这个方法有清除标记的功能;如果一个线程打上中断标记,第一次调用该方法如果返回值为true,那么第二次调用该方法返回值一定为false,因为第一次调用完毕会清除线程的中断标记。这个方法有两个大坑一个一个看。
class MyThread extends Thread{
@Override
public void run() {
for(int i = 1;i <= 500;i++){
System.out.println(Thread.currentThread().getName() + " -> " + i);
}
}
}
public class Test1 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(1);
myThread.interrupt(); //自己的线程停止
//其实虽然是myThread调用的这个方法,但是这个方法测试的是main线程是否有停止
//而从始至终我们都没有对main做任何的停止,所以两次都是false。
//具体可以看下面分析,这是一个大坑。
System.out.println("1. 是否停止?" + myThread.interrupted()); //返回值为false
System.out.println("2. 是否停止?" + myThread.interrupted()); //返回值为false
System.out.println("zzzzzzz~");
}
}
实例化对象.interrupted() == Thread.interrupted();因为在Thread.java类中调用静态static方法时大多数是针对currentThread()线程进行操作的。通俗的说就是这条语句写在哪个线程里就是测试哪个线程的状态。有点身在曹营心在汉的感觉。
public class Test2 {
public static void main(String[] args) {
MyThread my = new MyThread();
my.start();
/*
这里对main线程进行中断,然后判断main的状态。
1. 第一次输出true第二次输出false,说明interrupted方法有清除标记的功能
2. 测试的时候注释另一组,最后得到的第一组和第二组的结果是一样的。
3. 这就说明的了my.interrupted() == Thread.interrupted().
4. 主要原因是因为这是静态方法。直接看这条语句写在哪个线程里,就是哪个线程的。
*/
Thread.currentThread().interrupt(); //main线程停止
//第一组
//System.out.println("1. 是否停止?" + my.interrupted()); //返回值为true
//System.out.println("2. 是否停止?" + my.interrupted()); //返回值为false
//第二组
System.out.println("1. 是否停止?" + Thread.interrupted()); //返回值为true
System.out.println("2. 是否停止?" + Thread.interrupted()); //返回值为false
}
}
- public boolean this.isInterrupted();测试this关键在所在类的对象是否已经中断。这个方法不会像上面一样清除标记,即没有清除标记的功能。这个方法和上面的不一样,它是一个非静态方法,并且返回值是针对调用该方法的实例化对象线程状态的,并且不会清除标记。
public class Test3 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(1);
myThread.interrupt();
System.out.println("1. 是否停止?" + myThread.isInterrupted()); //返回值为true
System.out.println("2. 是否停止?" + myThread.isInterrupted()); //返回值为true
System.out.println("1. main是否停止?" + Thread.currentThread().isInterrupted()); //false
System.out.println("1. main是否停止?" + Thread.currentThread().isInterrupted()); //false
System.out.println("zzzzzzz~");
}
}
3. 正确停止线程的方法
- 异常法,对于我们不想要继续执行这个线程了我们可以采用手动制造异常的形式将这个线程通过try-catch异常捕捉停止下来。
class MyThread extends Thread{
@Override
public void run() {
try{
for(int i = 1;i <= 500;i++){
if(Thread.currentThread().isInterrupted()){ //建议使用这个方法,上面的方法有点玄学不好理解。
System.out.println("线程开始停止!");
throw new InterruptedException();
}
System.out.println(Thread.currentThread().getName() + " -> " + i);
}
} catch (InterruptedException e){
System.out.println("已经停止并且进入catch, 马上退出!");
//e.printStackTrace();
}
}
}
public class Test1 {
public static void main(String[] args) {
try {
MyThread m = new MyThread();
m.start();
Thread.sleep(2);
m.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 冲突异常法,上面是我们手动抛出异常制造线程停止,其实我们可以利用sleep + interrupt方法的冲突产生异常进行终止。因为无论是出于睡眠状态的线程被打扰中断,还是打扰中断的线程进入睡眠都会产生异常,也就是说sleep和interrupt先执行哪个都会有异常。
class MyThread2 extends Thread{
@Override
public void run() {
try{
for(int i = 1;i <= 500;i++){
if(Thread.currentThread().isInterrupted()){
System.out.println("线程已经被标记中断状态, 开始睡眠!");
Thread.sleep(5);
}
System.out.println(Thread.currentThread().getName() + " -> " + i);
}
} catch (InterruptedException e){
System.out.println("已经停止并且进入catch, 马上退出!");
//e.printStackTrace();
}
}
}
public class Test2 {
public static void main(String[] args) {
try {
MyThread2 m = new MyThread2();
m.start();
Thread.sleep(2);
m.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
另外先sleep再使用interrupt进行停止也是可以的。
- 使用return ;方法进行停止。这种方法也不建议使用,因为代码容易出现冗余,可维护性和扩展性都比较差。
class MyThread3 extends Thread{
@Override
public void run() {
for(int i = 1;i <= 500;i++){
if(Thread.currentThread().isInterrupted()){
System.out.println("线程已经被标记中断状态,通过return结束");
return ;
}
System.out.println(Thread.currentThread().getName() + " -> " + i);
}
}
}
public class Test3 {
public static void main(String[] args) {
try {
MyThread3 m = new MyThread3();
m.start();
Thread.sleep(2);
m.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- stop方法是删除线程状态,这个方法是一个废弃的方法虽然能够停止一个线程的运行,但是它极度不安全也不可靠,容易造成业务处理的不确定性。因为我们不知道它到底是执行到一个什么程度停止的。强烈不建议使用。
对于上面介绍的几种线程中断方式,只有第一和第二种方式比较好,个人感觉第一种方式最佳。直接抛出异常被捕获即可。