飞道的博客

如何优雅的停止线程?

323人阅读  评论(0)

0、面试场景


话说我真的信了,😂
面试官A:多线程有过了解么?
程序员B:有了解
面试官A:那线程是如何中断的呢?
面试官A:有多少中中断方式呢?
面试官A:有没有优雅的暂停方式呢?
程序员B:此时心态…

弱弱的回了一句不知道
面试官A:


1、interrupt 停不了?

package threadStop;

public class ThreadStop_1 {
  private static int count = 0;
  public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
      while (true) {
        try {
          System.out.println(count++);
          System.out.println("开始时间-> "+System.currentTimeMillis());
          System.out.println(Thread.currentThread().getName()+ " 睡眠中......");
          Thread.sleep(2_000);
          System.out.println("睡眠结束时间-> "+System.currentTimeMillis());
        } catch (InterruptedException e) {
          System.out.println(Thread.currentThread().getName() + " -> 被中断");
        }
      }
    }, "thread");
    t1.start();
    t1.interrupt();
  }
}

输出结果:
	输出结果:
    0
    开始时间-> 1589521867889
    thread 睡眠中......
    thread -> 被中断
    1
    开始时间-> 1589521867889
    thread 睡眠中......
    睡眠结束时间-> 1589521869889
    2
    开始时间-> 1589521869889
    thread 睡眠中......
    睡眠结束时间-> 1589521871890
    3
    开始时间-> 1589521871890
    thread 睡眠中......
    睡眠结束时间-> 1589521873891
    ...

结论:
   此时调用的 t1.interrupt() 并没有中断此线程 while 继续运行着
interrupt 方法干了什么?
   interrupt 方法其实只是改变了中断状态而已。而sleep、wait和join这些方法的内部会不断的检查中断状态的值,从而自己抛出InterruptEdException。
   所以,如果在线程进行其他处理时,调用了它的interrupt方法,线程也不会抛出InterruptedException的,只有当线程走到了sleep, wait, join这些方法的时候,才会抛出InterruptedException。 若是没有调用sleep, wait, join这些方法,或者没有在线程里自己检查中断状态,自己抛出InterruptedException,那InterruptedException是不会抛出来的。

2、interrupt + InterruptedException 能停止么?

public class ThreadStop_2 {
  private static int count = 0;
  public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
      // 注意1、!Thread.currentThread().isInterrupted()
      while (!Thread.currentThread().isInterrupted()) {
        try {
          System.out.println(count++);
          Thread.sleep(5_000);
        } catch (InterruptedException e) {
          System.out.println(Thread.currentThread().getName() + " -> 被中断");
          // 注意 2、 Thread.currentThread().interrupt();
          Thread.currentThread().interrupt();
          System.out.println("thread state -> " + Thread.currentThread().isInterrupted());
        }
      }
    }, "thread");
    t1.start();
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    t1.interrupt();
  }
}
输出结果:
 		0
  		thread -> 被中断
		thread state -> true

结论:
   因为线程在 sleep 的时候调用的 t1.interrupt() 则会抛出了 InterruptedException 异常,此时在异常中利用Thread.currentThread().interrupt() 或者 this.isInterrupted() 语句,while (this.isInterrupted()) 此时就会中断 while 循环。
   那为什么还需要在catch语句块中再调用一次 Thread.currentThread().interrupt() ,
因为如果该线程阻塞的调用 wait() ,wait(long) ,或wait(long, int)的方法Object类,或者在join() , join(long) , join(long, int) , sleep(long) ,或sleep(long, int) ,这个类的方法,那么它的中断状态将被清除,并且将收到一个InterruptedException 。
   所以说不再次调用 Thread.currentThread().interrupt() 的话那么 while (!Thread.currentThread().isInterrupted()) 中的 !Thread.currentThread().isInterrupted()一直都是 true

3、interrupt + throw new RuntimeException() 可以么?

package threadStop;

public class ThreadStop_3 {
  public static void main(String[] args) {
    Worker worker = new Worker();
    worker.start();
    try {
      Thread.sleep(150);
      worker.interrupt();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class Worker extends Thread {
  @Override
  public void run() {
    while (true) {
      if(this.isInterrupted()){
        System.out.println(this.getName());
        System.out.println("线程 "+ this.getName() +" 被中断");
        // 注意这里也可以换成return; 或者 break; 也可以中断异常
        throw new RuntimeException();
      }
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        this.interrupt();
      }
      System.out.println(1);
    }
  }
}
输出结果:
     1
     1
     Thread-0
     线程 Thread-0 被中断
     Exception in thread "Thread-0" java.lang.RuntimeException
     at threadStop.Worker.run(ThreadStop_2.java:33)

结论:
   方法说明:利用 throw new RuntimeException(); + 异常中的 this.interrupt(); 或者return;等中断异常,但是推荐使用 throw new RuntimeException();

4、如何更优雅的停止线程呢?

package threadStop;
public class ThreadStop_4 {
  public static void main(String[] args) {
    ThreadService service = new ThreadService();
    long startTime = System.currentTimeMillis();
    service.startUp(() ->{
      System.out.println("线程名称 -> " +Thread.currentThread().getName()+" 开始执行 ");
      while (true){
      // 假装做事情
      }
    });
    service.shutdown(10000);
    long end = System.currentTimeMillis();
    System.out.println(end - startTime);
  }
}

class ThreadService {
  private Thread executeThread;
  private boolean finished = false;

  public void startUp(Runnable task) {
    executeThread = new Thread() {
      @Override
      public void run() {
        // 创建守护线程
        Thread dThread = new Thread(task);
        dThread.setDaemon(true);
        dThread.start();
        try {
          // 等待守护线程执行完
          dThread.join();
          finished = true;
        } catch (InterruptedException e) {
          System.out.println("执行超时被打断");
          e.printStackTrace();
        }
      }
    };
    executeThread.start();
  }

  public void shutdown(long mills) {
    long currentTime = System.currentTimeMillis();
    while (!finished) {
      if ((System.currentTimeMillis() - currentTime) >= mills) {
        System.out.println("任务超时");
        executeThread.interrupt();
        break;
      }
      try {
        Thread.sleep(1);
      } catch (InterruptedException e) {
        System.out.println("执行线程被打断!");
        break;
      }
    }
    finished = false;
  }
}

输出结果:
       线程名称 -> Thread-1 开始执行
       任务超时
       10035
       执行超时被打断
       java.lang.InterruptedException
       at java.lang.Object.wait(Native Method)
       at java.lang.Thread.join(Thread.java:1252)
       at java.lang.Thread.join(Thread.java:1326)
       at threadStop.ThreadService$1.run(ThreadStop_1.java:37)

结论:
   利用守护线程去干当前非守护线程要做的事情,这样当主线程被打断后那么守护线程自己就停止了不需要再手动的去停止子线程了,如:Java垃圾回收线程就是一个典型的守护线程

5、interrupt 、isInterrupted、interrupted 你们在干嘛?

package threadStop;

public class ThreadStop_5 {
public static void main(String[] args) {
    Thread thread = new Thread(new ProducerThread());
    ProducerThread.startUp(thread);
    try {
      Thread.sleep(1000);
      ProducerThread.shutDown(thread);
    } catch (InterruptedException e) {
      System.out.println(e.getMessage());
    }
  }
}

class ProducerThread implements Runnable {
  // 中断信号量
  private volatile static boolean STOP_FLAG = true;
  
  @Override
  public void run() {
    System.out.println("线程名称 -> " + Thread.currentThread().getName() + " 线程状态 -> " + Thread.currentThread().getState());
    while (STOP_FLAG) {
      System.out.println(" thread name -> " + Thread.currentThread().getName());
      try {
        Thread.sleep(10_0000);
      } catch (InterruptedException e) {
        System.out.println(Thread.currentThread().getName()+"---isInterrupted---"+Thread.currentThread().isInterrupted());
        Thread.currentThread().interrupt();
        /** 测试当前线程是否中断。 该方法可以清除线程的中断状态 。 换句话说,
        如果这个方法被连续调用两次,那么第二个调用将返回false(除非当前线程再次中断)*/
        System.out.println("---interrupted--1-"+Thread.interrupted());
        Thread.currentThread().interrupt();
        System.out.println(Thread.currentThread().getName()+ " -> 又一次被中断 ");
        System.out.println("---interrupted--2-"+Thread.interrupted());
        System.out.println(Thread.currentThread().getName()+ " ->中断状态被方法 Thread.interrupted() 清除了");
	    /** 中断状态被方法 Thread.interrupted() 清除了所以
	    Thread.currentThread().isInterrupted() 为True */
        System.out.println(Thread.currentThread().getName()+"---isInterrupted---"+Thread.currentThread().isInterrupted());
        e.printStackTrace();
      }
    }
  }
  public static void startUp(Thread thread) {
    thread.start();
  }
  public static void shutDown(Thread thread) {
    STOP_FLAG = false;
    thread.interrupt();
  }
}
输出结果:
       线程名称 -> Thread-0 线程状态 -> RUNNABLE
       thread name -> Thread-0
       Thread-0---isInterrupted---false
       ---interrupted--1-true
       Thread-0 -> 又一次被中断
       ---interrupted--2-true
       Thread-0 ->中断状态被方法 Thread.interrupted() 清除了
       Thread-0---isInterrupted---false
       java.lang.InterruptedException: sleep interrupted
       at java.lang.Thread.sleep(Native Method)
       at threadStop.ProducerThread.run(ThreadStop.java:55)
       at java.lang.Thread.run(Thread.java:748)

结论:
1、interrupt方法干了什么?
   interrupt方法其实只是改变了中断状态而已。而sleep、wait和join这些方法的内部会不断的检查中断状态的值,从而自己抛出InterruptEdException。所以,如果在线程进行其他处理时,调用了它的interrupt方法,线程也不会抛出InterruptedException的,只有当线程走到了sleep, wait, join这些方法的时候,才会抛出InterruptedException。若是没有调用sleep, wait, join这些方法,或者没有在线程里自己检查中断状态,自己抛出InterruptedException,那InterruptedException是不会抛出来的。
2、isInterrupted 方法干了什么
    Interrupted 测试这个线程是否被中断。 线程的中断状态不受此方法的影响。
忽略线程中断,因为线程在中断时不存在将被该方法返回false所反映。
3、interrupted方法干了什么
   测试当前线程是否中断。 该方法可以清除线程的中断状态 。 换句话说,如果这个方法被连续调用两次, 那么第二个调用将返回false(除非当前线程再次中断,在第一个调用已经清除其中断状态之后,在第二个调用之前已经检查过)。由于此方法返回false,因此将反映线程中断,因为该线程在中断时尚未处于活动状态而被忽略
   总之 interrupt 的作用就是需要用户自己去监视线程的状态位并做处理。

创作不易关注三连击,我会一直持续输出… 期待下一期的多线程的学习 谢谢!
与君共勉 …


转载:https://blog.csdn.net/weixin_38071259/article/details/106142392
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场