<
>

Java线程的停止实现原理详解

2023-01-29 08:47:05 来源:易采站长站 作者:

目录
线程停止的原理如何正确停止线程在普通情况下停止线程在阻塞情况下停止线程线程在每次迭代后都阻塞停止线程的最佳实践错误停止的方法被弃用的stop,suspend和resume方法用volatile设置boolean标记位interrupt源码查看interrupt相关函数练习

线程停止的原理

使用interrupt来通知,而不是强制

java提供了interrrupt让一个线程来通知另一个线程停止

如果想中断一个线程,但是那个线程不想去中断,那就无能为力,我们没有强制去中断线程的手段,因为线程停止前需要做一定的收尾工作

所以正确停止线程,是如何用interrupt来通知那个线程,以及被停止的线程如何进行配合

如何正确停止线程

在普通情况下停止线程

代码展示

    调用interrupt没有作用下面这段代码,执行interrupt之后,线程并没有被中断因为被执行的线程并没有相应中断的方式
    public class stopThreadWithoutSleep  implements Runnable{
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new stopThreadWithoutSleep());
            thread.start();
            Thread.sleep(1000);
            thread.interrupt();
        }
        @Override
        public void run() {
            int num = 0;
            while(num <= Integer.MAX_VALUE / 2) {
                if (num % 10000 == 0) {
                    System.out.println(num + "是10000的倍数");
                }
                num++;
            }
            System.out.println("结束");
        }
    }
    /*
    由于太长,只展示结尾部分的结果 
    1073710000是10000的倍数
    1073720000是10000的倍数
    1073730000是10000的倍数
    1073740000是10000的倍数
    结束
    * */
    
      被执行线程加上相应中断的操作之后结果可知,被执行线程相应一秒之后就结束了
      public class stopThreadWithoutSleep  implements Runnable{
          public static void main(String[] args) throws InterruptedException {
              Thread thread = new Thread(new stopThreadWithoutSleep());
              thread.start();
              Thread.sleep(1000);
              thread.interrupt();
          }
          @Override
          public void run() {
              int num = 0;
              while(!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
                  if (num % 10000 == 0) {
                      System.out.println(num + "是10000的倍数");
                  }
                  num++;
              }
              System.out.println("结束");
          }
      }
      /*
      由于太长,只展示结尾部分的结果
      587830000是10000的倍数
      587840000是10000的倍数
      587850000是10000的倍数
      587860000是10000的倍数
      结束
      * */
      

      在阻塞情况下停止线程

      代码展示

        中断之后,抛出异常线程在sleep的过程中,会catch到InterruptedException这个异常,从而相应中断
        public class stopThreadWithSleep {
            public static void main(String[] args) {
                Runnable runnable = () -> {
                    int num = 0;
                    while (num <= 300 && !Thread.currentThread().isInterrupted()) {
                        if (num % 100 == 0) {
                            System.out.println(num + "是100的倍数");
                        }
                        num++;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                };
                Thread thread = new Thread(runnable);
                thread.start();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                thread.interrupt();
            }
        }
        /*
        * 0是100的倍数
        100是100的倍数
        200是100的倍数
        300是100的倍数
        java.lang.InterruptedException: sleep interrupted
           at java.lang.Thread.sleep(Native Method)
           at com.jx.JavaTest.stopThread.stopThreadWithSleep.lambda$main$0(stopThreadWithSleep.java:15)
           at java.lang.Thread.run(Thread.java:748)
        * */
        

        线程在每次迭代后都阻塞

          代码展示>
          public class stopThreadWithSleepEveryLoop {
              public static void main(String[] args) {
                  Runnable runnable = () -> {
                      int num = 0;
                      try {
                          while (num <= 10000) {
                              if (num % 100 == 0) {
                                  System.out.println(num + "是100的倍数");
                              }
                              num++;
                              Thread.sleep(10);
                          }
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  };
                  Thread thread = new Thread(runnable);
                  thread.start();
                  try {
                      Thread.sleep(5000);
                  } catch (
                          InterruptedException e) {
                      e.printStackTrace();
                  }
                  thread.interrupt();
              }
          }
          /*
          * 0是100的倍数
          100是100的倍数
          200是100的倍数
          300是100的倍数
          java.lang.InterruptedException: sleep interrupted
             at java.lang.Thread.sleep(Native Method)
             at com.jx.JavaTest.stopThread.stopThreadWithSleepEveryLoop.lambda$main$0(stopThreadWithSleepEveryLoop.java:15)
             at java.lang.Thread.run(Thread.java:748)
          Process finished with exit code 0
          * 
          * */
          

          当catch写到while内,则不能正常中断

          public class CantInterrupt {
              public static void main(String[] args) {
                  Runnable runnable = () -> {
                      int num = 0;
                      while (num <= 10000) {
                          if (num % 100 == 0) {
                              System.out.println(num + "是100的倍数");
                          }
                          num ++;
                          try {
                              Thread.sleep(10);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                  };
                  Thread thread = new Thread(runnable);
                  thread.start();
                  try {
                      Thread.sleep(5000);
                  } catch (
                          InterruptedException e) {
                      e.printStackTrace();
                  }
                  thread.interrupt();
              }
          }
          /*
          * 0是100的倍数
          100是100的倍数
          200是100的倍数
          300是100的倍数
          java.lang.InterruptedException: sleep interrupted
             at java.lang.Thread.sleep(Native Method)
             at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)
             at java.lang.Thread.run(Thread.java:748)
          400是100的倍数
          500是100的倍数
          600是100的倍数
          700是100的倍数
          800是100的倍数
          900是100的倍数
          Process finished with exit code -1
          * */
          
            即使在while的判断条件中,加上检测中断的机制,也不能正常中断因为java的sleep函数,一旦相应中断,就会将中断的标志位删除
            public class CantInterrupt {
                public static void main(String[] args) {
                    Runnable runnable = () -> {
                        int num = 0;
                        while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
                            if (num % 100 == 0) {
                                System.out.println(num + "是100的倍数");
                            }
                            num++;
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    };
                    Thread thread = new Thread(runnable);
                    thread.start();
                    try {
                        Thread.sleep(5000);
                    } catch (
                            InterruptedException e) {
                        e.printStackTrace();
                    }
                    thread.interrupt();
                }
            }
            /*
            0是100的倍数
            100是100的倍数
            200是100的倍数
            300是100的倍数
            java.lang.InterruptedException: sleep interrupted
               at java.lang.Thread.sleep(Native Method)
               at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)
               at java.lang.Thread.run(Thread.java:748)
            400是100的倍数
            500是100的倍数
            Process finished with exit code -1
            * */
            

            停止线程的最佳实践

              在方法签名中抛出异常,在run方法中强制进行try>
              public class StopThreadInProd implements Runnable{
                  public static void main(String[] args) throws InterruptedException {
                      Thread thread = new Thread(new StopThreadInProd());
                      thread.start();
                      Thread.sleep(1000);
                      thread.interrupt();
                  }
                  @Override
                  public void run() {
                      while (true) {
                          System.out.println("start");
                          try {
                              throwInMethod();
                          } catch (InterruptedException e) {
                              System.out.println("保存日志/关闭程序");
                              e.printStackTrace();
                          }
                      }
                  }
                  private void throwInMethod() throws InterruptedException {
                          Thread.sleep(2000);
                  }
              }
              /*
              * start
              保存日志/关闭程序
              start
              java.lang.InterruptedException: sleep interrupted
                 at java.lang.Thread.sleep(Native Method)
                 at com.jx.JavaTest.stopThread.StopThreadInProd.throwInMethod(StopThreadInProd.java:26)
                 at com.jx.JavaTest.stopThread.StopThreadInProd.run(StopThreadInProd.java:17)
                 at java.lang.Thread.run(Thread.java:748)
              start
              start
              Process finished with exit code -1
              * 
              * */
              
                在catch语句中调用Thread.currentThread().interrupt恢复中断状态结果:抛出异常,程序结束
                public class StopThreadInProd2 implements Runnable{
                    public static void main(String[] args) throws InterruptedException {
                        Thread thread = new Thread(new StopThreadInProd2());
                        thread.start();
                        Thread.sleep(1000);
                        thread.interrupt();
                    }
                    @Override
                    public void run() {
                        while (true) {
                            if (Thread.currentThread().isInterrupted()) {
                                System.out.println("Interrupt");
                                break;
                            }
                            reInterrupt();
                        }
                    }
                    private void reInterrupt() {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            e.printStackTrace();
                        }
                    }
                }
                /*
                java.lang.InterruptedException: sleep interrupted
                   at java.lang.Thread.sleep(Native Method)
                   at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)
                   at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)
                   at java.lang.Thread.run(Thread.java:748)
                Interrupt
                *
                * */
                
                  依照上面的代码,如果方法没有没有重新抛出异常结果:程序抛出异常,但是程序没有停止运行
                  public class StopThreadInProd2 implements Runnable{
                      public static void main(String[] args) throws InterruptedException {
                          Thread thread = new Thread(new StopThreadInProd2());
                          thread.start();
                          Thread.sleep(1000);
                          thread.interrupt();
                      }
                      @Override
                      public void run() {
                          while (true) {
                              if (Thread.currentThread().isInterrupted()) {
                                  System.out.println("Interrupt");
                                  break;
                              }
                              reInterrupt();
                          }
                      }
                      private void reInterrupt() {
                          try {
                              Thread.sleep(2000);
                          } catch (InterruptedException e) {
                  //            Thread.currentThread().interrupt();
                              e.printStackTrace();
                          }
                      }
                  }
                  /*
                  java.lang.InterruptedException: sleep interrupted
                     at java.lang.Thread.sleep(Native Method)
                     at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)
                     at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)
                     at java.lang.Thread.run(Thread.java:748)
                  *
                  * */
                  

                  错误停止的方法

                  被弃用的stop,suspend和resume方法

                    使用stop停止线程,会导致线程运行一半突然停止,没办法完成最基本的操作,会造成脏数据下面这段代码的结果会造成一个连队只有部分人领取到了装备stop是不安全的,会直接停止监视器suspend和resume不会破坏对象,但是会让线程挂起,不释放锁,容易造成死锁
                    public class StopThread implements Runnable{
                        @Override
                        public void run() {
                            // 模拟指挥军队,一共五个连队,每个连队一百人
                            // 以连队为单位发放武器
                            for (int i = 0; i < 5; i++) {
                                System.out.println("连队" + i + "领取武器");
                                for (int j = 0; j < 10; j++) {
                                    System.out.println(j);
                                    try {
                                        Thread.sleep(50);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                System.out.println("连队" + i + "领取完毕");
                            }
                        }
                        public static void main(String[] args) {
                            Thread thread = new Thread(new StopThread());
                            thread.start();
                            try {
                                thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            thread.stop();
                        }
                    }
                    /*
                    * 连队0领取武器
                    0
                    1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    连队0领取完毕
                    连队1领取武器
                    0
                    1
                    2
                    3
                    4
                    5
                    
                    Process finished with exit code 0
                    
                    * */
                    

                    用volatile设置boolean标记位

                      下面这段代码,通过改变标志位的值会成功终止线程
                      public class Volatile implements Runnable {
                          private volatile boolean canceled = false;
                          @Override
                          public void run() {
                              int num = 0;
                              try {
                                  while (num <= 10000 && !canceled) {
                                      if (num % 100 == 0) {
                                          System.out.println(num + " 是100的倍数");
                                      }
                                      num++;
                                      Thread.sleep(1);
                                  }
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                          public static void main(String[] args) throws InterruptedException {
                              Volatile v = new Volatile();
                              Thread thread = new Thread(v);
                              thread.start();
                              Thread.sleep(1000);
                              v.canceled = true;
                          }
                      }
                      /*
                      *0 是100的倍数
                      100 是100的倍数
                      200 是100的倍数
                      300 是100的倍数
                      400 是100的倍数
                      500 是100的倍数
                      600 是100的倍数
                      Process finished with exit code 0
                      * 
                      * */
                      
                        当陷入阻塞的时候,是无法停止线程的下面这段代码的运行结果,并没有打印生产者停止运行,说明根本没有执行生产者的finally那部分代码同时程序也没停止原因见生产者代码 while循环中的注释
                        // 模拟生产者和消费者
                        public class cantStop {
                            public static void main(String[] args) throws InterruptedException {
                                // 阻塞队列
                                // 满了之后,放不进去
                                // 空的时候取数据,也会堵塞
                                ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
                                Producer producer = new Producer(storage);
                                Thread producerThread = new Thread(producer);
                                producerThread.start();
                                Thread.sleep(1000);
                                Consumer consumer = new Consumer(storage);
                                while (consumer.needMore()) {
                                    System.out.println(consumer.storage.take() + "被消费");
                                    Thread.sleep(100);
                                }
                                System.out.println("消费者不需要更多数据");
                                // 消费者不需要数据,让生产者停下来
                                producer.canceled = true;
                            }
                        }
                        // 生产者
                        class Producer implements Runnable {
                            public volatile boolean canceled = false;
                            BlockingQueue storage;
                            public Producer(BlockingQueue storage) {
                                this.storage = storage;
                            }
                            @Override
                            public void run() {
                                int num = 0;
                                try {
                                    while (num <= 10000 && !canceled) {
                                        if (num % 100 == 0) {
                                            // 当堵塞队列满了之后,会堵塞在这里,而这段代码没有判断机制
                                            storage.put(num);
                                            System.out.println("num" + "生产");
                                        }
                                        num++;
                                    }
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                } finally {
                                    System.out.println("生产者停止运行");
                                }
                            }
                        }
                        // 消费者
                        class Consumer {
                            BlockingQueue storage;
                            public Consumer(BlockingQueue storage) {
                                this.storage = storage;
                            }
                            public boolean needMore() {
                                if (Math.random() > 0.9) {
                                    return false;
                                }
                                return true;
                            }
                        }
                        /*
                        * num生产
                        num生产
                        num生产
                        num生产
                        num生产
                        num生产
                        num生产
                        num生产
                        num生产
                        num生产
                        0被消费
                        num生产
                        消费者不需要更多数据
                        
                        * 
                        * */
                        
                          将上面代码用interrupt进行中断程序成功停止
                          public class finxed {
                              public static void main(String[] args) throws InterruptedException {
                                  finxed finxed = new finxed();
                                  // 阻塞队列
                                  // 满了之后,放不进去
                                  // 空的时候取数据,也会堵塞
                                  ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
                                  Producer producer = finxed.new Producer(storage);
                                  Thread producerThread = new Thread(producer);
                                  producerThread.start();
                                  Thread.sleep(1000);
                                  Consumer consumer = finxed.new Consumer(storage);
                                  while (consumer.needMore()) {
                                      System.out.println(consumer.storage.take() + "被消费");
                                      Thread.sleep(100);
                                  }
                                  System.out.println("消费者不需要更多数据");
                                  // 消费者不需要数据,让生产者停下来
                                  producerThread.interrupt();
                              }
                              class Producer implements Runnable {
                                  public volatile boolean canceled = false;
                                  BlockingQueue storage;
                                  public Producer(BlockingQueue storage) {
                                      this.storage = storage;
                                  }
                                  @Override
                                  public void run() {
                                      int num = 0;
                                      try {
                                          while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
                                              if (num % 100 == 0) {
                                                  storage.put(num);
                                                  System.out.println("num" + "生产");
                                              }
                                              num++;
                                          }
                                      } catch (InterruptedException e) {
                                          e.printStackTrace();
                                      } finally {
                                          System.out.println("生产者停止运行");
                                      }
                                  }
                              }
                              class Consumer {
                                  BlockingQueue storage;
                                  public Consumer(BlockingQueue storage) {
                                      this.storage = storage;
                                  }
                                  public boolean needMore() {
                                      if (Math.random() > 0.9) {
                                          return false;
                                      }
                                      return true;
                                  }
                              }
                          }
                          /*
                          * 2100被消费
                          num生产
                          2200被消费
                          num生产
                          2300被消费
                          num生产
                          消费者不需要更多数据
                          生产者停止运行
                          java.lang.InterruptedException
                             at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
                             at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
                             at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353)
                             at com.jx.JavaTest.stopThread.volatiledmo.finxed$Producer.run(finxed.java:51)
                             at java.lang.Thread.run(Thread.java:748)
                          Process finished with exit code 0
                          * 
                          * */

                          interrupt源码查看

                            这段代码做的都是一些判断,真正执行中断的代码时interrupt0interrupt0是native代码
                            public void interrupt() {
                                if (this != Thread.currentThread())
                                    checkAccess();
                                synchronized (blockerLock) {
                                    Interruptible b = blocker;
                                    if (b != null) {
                                        interrupt0();           // Just to set the interrupt flag
                                        b.interrupt(this);
                                        return;
                                    }
                                }
                                interrupt0();
                            }
                            private native void interrupt0();
                            

                            interrupt相关函数练习

                              isInterrupted获取中断标志,获取的是前面的线程interrupted获取中断标志并重置,只关心执行的线程,所以下面代码执行的是main线程
                              public class InterruptedTest {
                                  public static void main(String[] args) throws InterruptedException {
                                      Thread thread = new Thread(new Runnable() {
                                          @Override
                                          public void run() {
                                              while (true) {
                              
                                              }
                                          }
                                      });
                                      thread.start();
                                      thread.interrupt();
                                      // 获取中断标志
                                      System.out.println(thread.isInterrupted()); // true
                                      // 获取中断标志并重置
                                      System.out.println(thread.interrupted()); //false
                                      System.out.println(Thread.interrupted()); // false
                                      System.out.println(thread.isInterrupted()); //true
                                      thread.join();
                                      System.out.println("over");
                                  }
                              }
                              

                              到此这篇关于Java线程的停止实现原理详解的文章就介绍到这了,更多相关Java线程的停止内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!

暂时禁止评论

微信扫一扫

易采站长站微信账号