【问题标题】:How to interrupt an Infinite Loop如何中断无限循环
【发布时间】:2012-08-13 16:55:03
【问题描述】:

虽然我知道问起来有点傻,但我还是想进一步了解它的技术角度。

一个简单的无限循环示例:

public class LoopInfinite {
    public static void main(String[] args) {
        for (;;) {
            System.out.println("Stack Overflow");
        }
    }
}

如何从此类外部中断(停止)这个无限循环(例如,借助继承)?

【问题讨论】:

  • 拔掉jvm的插头:P
  • 您是否尝试将其关闭再打开?
  • 这让我想起了某个Kōan,Interrupt
  • 嗯,首先你应该写一些东西来决定这个循环(或任何其他循环)是永远运行还是最终停止......
  • 如果它是无限的,那么根据定义它不会被中断:-P

标签: java loops infinite-loop


【解决方案1】:

写到这里我都觉得很脏,但是……

在不同的线程中,您可以使用 PrintStream 实现调用 System.setOut(),当您调用 println() 时会抛出 RuntimeException

【讨论】:

  • System.setOut(null) 就足够了。
  • @MarkoTopolnik 也许另一个具有main 方法的类只是调用了LoopInfinite.main(null) 或类似的东西。我们不知道,只有OP知道……
  • @maba 这是一个非常牵强的假设。当提问者发布涉及main 的代码时,我们可以很确定他的意思是这是他的切入点。否则,为什么不假设有一个 AOP 基础架构允许向每个PrintStream.println 调用注入任意代码,或者System 实际上是一个具有完全不相关实现的import my.package.System
  • @Marko 我认为您可能过多地研究方法问题。为什么不将 Thread.stop 作为不同的答案发布,包括 Thread 访问权限。
  • @Marko 我希望这只是一个纯粹的学术问题。 ;)
【解决方案2】:

我们可以使用volatile 变量来实现它,我们将更改外部Thread 并停止循环。

   for(;!cancelled;) /*or while(!cancelled)*/{
       System.out.println("Stackoverflow");
   }

这是编写无限循环的更好方法。

public class LoopInfinite{
      private static volatile boolean cancelled=false;
      public static void main(String[] args){
             for(;!cancelled;) { //or while(!cancelled)
                    System.out.println("Stackoverflow");
             }
      }
      public void cancel(){
        cancelled=true;
      }
}

【讨论】:

  • 别忘了将 cancelled 声明为 volatile
  • 也许您也希望 cancel() 方法为 static。那么你不需要LoopInfinite 的对象就能停止循环。
  • @maba - 在这种情况下你是 r8。这是一个 sn-p 不是实际的实现。实际实施可能需要进行数千次更改。
  • @Buhb:除非您从多个线程读取/写入其他内存,并且必须与“已取消”标志一致,否则这不是必需的。如果这是你的情况,你最好使用锁/监视器或其他重量级并发控制,因为“易失性”本身可能对你没有帮助。而且,如果您确实正确使用了锁,那么在大多数情况下 volatile 就变得不必要了。避免使用它,除非您真正了解自己在做什么,直至 CLR 内存模型的级别以及存储/释放重新排序是什么。只需使用锁。
  • 为什么要为此使用任何局部变量,而不是本机的中断()支持?简单地写while(!isInterrupted()) 将一直持续到有人在线程上调用interrupt() 方法。
【解决方案3】:

可以从另一个线程获取运行无限循环的线程并在其上调用中断。不过,您必须非常确定自己在做什么,并希望被中断的线程在被中断时能够正常运行。

在这里,我用有问题的循环命名了线程,以便于识别。请注意,以下解决方案容易受到竞争条件的影响。

    Thread loop = new Thread() { 

        public void run() {
            Thread.currentThread().setName("loop");
            while(true) {
                System.out.print(".");
            }
        }
    }.start();

然后在其他类中:

    ThreadGroup group = Thread.currentThread().getThreadGroup();
    Thread[] threads = new Thread[group.activeCount()];
    group.enumerate(threads);

    for(Thread t : threads) {
        if(t.getName().equals("loop")) {
            /* Thread.stop() is a horrible thing to use. 
               Use Thread.interrupt() instead if you have 
               any control over the running thread */
            t.stop();
        }
    }

请注意,在我的示例中,我假设两个线程在同一个线程组中。无法保证会出现这种情况,因此您可能需要遍历更多组。

如果您对此有一定的控制权,一个不错的模式是在循环声明中使用while(!isInterrupted()),并使用t.interrupt() 而不是t.stop()

我对您的唯一建议是,即使在发布此内容之后,也不要这样做。你可以这样做,但你真的不应该

【讨论】:

  • 当您调用Thread.currentThread().setName("loop"); 时,您实际上不是还在主循环中并为它设置一个新名称吗?我通常不会覆盖Thread 类的start() 方法。
  • 我不确定您的循环是否会真正停止。在循环中,您必须执行if (Thread.interrupted()) break;。您可以在此处阅读有关线程中断的信息:docs.oracle.com/javase/tutorial/essential/concurrency/…
  • 修改为使用 Thread.stop() 代替,因为你是对的。我一厢情愿地认为我可以避免 stop() ;)
【解决方案4】:

我认为这是不可能的。仅在循环中使用 break。你可以使用

while(cond) {}

从其他地方把它弄成 false

【讨论】:

    【解决方案5】:

    您可以通过从 Thread.currentThread() 中询问来保持其对这个线程 [main] 的继承引用的静态引用来中断这个线程,就像这样

    public class LoopInfinite{
    public static Thread main = null;
    public static void main(String[] args){
        main = Thread.currentThread();
        for(;;)
           System.out.println("Stackoverflow");
        }
    }
    

    要终止,您可以从其他线程调用它

    LoopInfinite.main.interrupt();
    

    但它只有在两个线程都属于同一个组时才有效。否则调用线程会得到SecurityException

    【讨论】:

      【解决方案6】:

      你不能从这个类之外阻止它。如果你使用继承,你可以覆盖你的循环,但没有 abort-flag 你将无法这样做。

      【讨论】:

      • 在这种情况下你不能使用继承,因为它是一个静态方法。
      • 我的意思是,在一个简单的类中编写了这个无限循环代码,那么我可以通过什么方式停止这个无限函数
      【解决方案7】:

      非常开放的问题,但停止这样的循环很可能需要您从另一个线程进行操作。然后,另一个线程将需要设置一些您的无限循环可以定期检查的变量,以及该变量是否具有特定值;跳出循环。

      【讨论】:

        【解决方案8】:

        如果不完全停止该过程,您将无法中断此特定循环。通常,如果您尝试从外部源执行此操作(我假设您无法控制源代码,因为如果您这样做了,您可以轻松地在循环中设置一个条件,例如您可以设置的布尔值从外部线程),您将不得不停止正在运行的线程,无论您是通过Thread object 执行此操作(您必须以某种方式找到对它的引用,例如通过循环通过existing Threads),或者您是否将其作为system process 停止。

        另一种选择是使用不是无限循环的循环覆盖该方法,但不幸的是,这不适用于您的示例,因为它是一个静态方法。

        【讨论】:

        • 为了简单起见,假设我没有使用 main();在一个简单的类中只写了这段无限代码,...然后
        • 那么如果你不能改变代码,你就必须找到它正在运行的线程,这可能是从它创建时对它的引用,或者你可以循环通过所有线程并尝试确定哪个是合适的,但这可能很困难。如果你更清楚地指定具体是什么情况(循环是如何开始的,你究竟需要发生什么等等,例如循环是由你控制的代码开始的吗?)
        【解决方案9】:

        您的问题看起来像线程问题。但是,即使在线程中包含停止标志,现在也是一个好习惯

        【讨论】:

          【解决方案10】:

          如果你需要一个“无限”循环,你肯定需要一个线程(否则你的应用会一直卡住直到循环结束)。

          class BigLoop extends Thread
          {
          
              private boolean _sexyAndAlive = true;
          
              // make some constructor !
          
              public void softTerminate()
              {
              _sexyAndAlive = false;
              }
          
          
              public void run()
              {
                  try
                  {
                      while( _sexyAndAlive )
                      {
                         // Put your code here 
                      }
                  }
                  catch( Some Exceptions ... )
                  {
                      // ...
                  }
                  // put some ending code here if needed
              }
          }
          
          
          // in another file :
          
          BigLoop worker = new BigLoop();
          worker.start(); // starts the thread
          
          // when you want to stop it softly
          worker.softTerminate();
          

          所以,这是一个让后台运行循环的简单方法。

          【讨论】:

          • 如果您知道大概需要多少循环(例如几千个),您也可以设置一个最大计数:int i=0, maxCount=10000; while(true){doSomething();i++;if(i>maxCount){break;}} // 所以你永远不会超过 10k 循环
          【解决方案11】:

          添加一个变量 shouldBreak 或者可以使用 getter 和 setter 设置的东西。

          public class LoopInfinite {
          private boolean shouldBreak = false;
          
          public boolean isShouldBreak() {
              return shouldBreak;
          }
          
          public void setShouldBreak(boolean shouldBreak) {
              this.shouldBreak = shouldBreak;
          }
          
          public static void main(String[] args) {
              // Below code is just to simulate how it can be done from out side of
              // the class
              LoopInfinite infinite = new LoopInfinite();
              infinite.setShouldBreak(true);
              for (;;) {
                  System.out.println("Stackoverflow");
                  if (infinite.shouldBreak)
                      break;
              }
          }
          

          }

          【讨论】:

            【解决方案12】:

            这是我所做的:

            while(Exit == false){
                Scanner input = new Scanner(System.in);
                String in = input.next();
            
                switch(in){
                    case "FindH": 
                        FindHyp hyp = new FindHyp();
                        float output = hyp.findhyp();
                        System.out.println(output); 
                    case "Exit":
                        Exit = true;
                break;
            
                }
              }  
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-04-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多