需要一个基于Java的可中断计时器线程。

4
我是一名有帮助的助手,可以为您进行翻译。以下是需要翻译的内容:

我有一个主程序,它在目标设备(智能手机)上运行脚本,并在while循环中等待stdout消息。然而,在这种特殊情况下,stdout上的某些心跳消息可能会间隔近45秒到1分钟。

类似于:

stream = device.runProgram(RESTORE_LOGS, new String[] {});
stream.flush();
String line = stream.readLine();
while (line.compareTo("") != 0) {
    reporter.commentOnJob(jobId, line);
    line = stream.readLine();
}    

所以,我想在从标准输出读取一行后启动一个可中断的新线程,并需要一个睡眠窗口。在能够读取新行后,我想能够中断/停止(有问题无法杀死进程),处理stdout文本的换行符并重新启动进程。

如果在计时器窗口内(比如45秒)无法读取一行,则希望有一种方法退出while循环。

我已经尝试了thread.run、thread.interrupt方法。但是遇到了杀死和启动新线程的问题。

这是最好的解决方案还是我漏掉了一些明显的东西?

2个回答

3

看起来,System.in 的实现在不同平台上存在很大差异,并且特别是并不总是提供可中断或异步关闭的功能。

以下是一种解决方法,它不依赖于这些特性,但代价是无法正确清理;如果在超时到期之前没有接收到输入,则 Consumer 线程将处于阻塞的 read() 状态。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

class InterruptInput
{

  private static final String EOF = new String();

  private final SynchronousQueue<String> pipe = new SynchronousQueue<String>();

  private final BufferedReader input;

  private final long timeout;

  InterruptInput(BufferedReader input, long timeout)
  {
    this.input = input;
    this.timeout = timeout;
  }

  public static void main(String... argv)
    throws Exception
  {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    InterruptInput input = 
      new InterruptInput(in, 5000);
    input.read();
  }

  void read()
    throws InterruptedException
  {
    System.out.println("Enter lines of input (or empty line to terminate):");
    Thread t = new Consumer();
    t.start();
    while (true) {
      String line = pipe.poll(timeout, TimeUnit.MILLISECONDS);
      if (line == EOF)
        break;
      if (line == null) {
        System.out.println("Input timed-out.");
        t.interrupt();
        break;
      }
      System.out.println("[input]: " + line);
    }
  }

  private class Consumer
    extends Thread
  {

    Consumer()
    {
      setDaemon(true);
    }

    @Override
    public void run()
    {
      while (!Thread.interrupted()) {
        String line;
        try {
          line = input.readLine();
        }
        catch (IOException ex) {
          throw new RuntimeException(ex);
        }
        try {
          if ((line == null) || (line.length() == 0)) {
            pipe.put(EOF);
            break;
          }
          else {
            pipe.put(line);
          }
        }
        catch (InterruptedException ex) {
          break;
        }
      }
    }
  }

}

是的,它将终止循环,就像输入空行一样,并打印"完成"。这是您想要的吗?如果不是,您想要什么? - erickson
@LambeauLeap - 嗯,我正在运行Sun的1.6.0_20 JVM,并且它没有出现错误,这让我感到惊讶,因为我实际上预期会出现IOException...这就是你所看到的。在这种情况下,我将更新我的答案以显示如何平稳地终止循环。 - erickson
谢谢您的确认,也许在此期间我会继续完善基于您之前提到的ScheduledExecutorService库的解决方案! - LambeauLeap
使用SyncQueue是一个好主意,但我担心你提到的清理条件并不那么理想。在我的情况下,我已经有了自己的Java库,该库提供了从设备(智能手机)读取的流。在正测试用例中,输入最终以<EMPTY>行结束,但我仍然进行轮询以从运行在设备上的脚本中读取returnStatusCode。现在,在这种情况下,我想知道是否使用SyncQueues会危及情况,以及您所说的其他清理是否被JAVA GC处理? - LambeauLeap
不,将null赋值给t不会起到任何作用。read()方法在当前的interrupt()之后立即返回,并且t所属的堆栈帧消失了,但是该Thread仍然会继续运行,阻塞在读取操作中。不过,正如我所说,这完全取决于底层的InputStream实现是否可以异步关闭。 - erickson
显示剩余20条评论

0

这确实看起来像是一个更优雅的解决方案,特别是考虑到我以前没有使用过ScheduledExecutorService。但我仍然在努力将所有部分整合在一起!我不确定工作线程何时被调用进行45秒倒计时?此外,我的意图是让这样的工作线程在遇到stdout行时重新启动倒计时,从而将倒计时重置为全新的45秒窗口。这有助于澄清吗。

虽然我正在努力将ScheduledExecutorService纳入我的解决方案,但这里是我使用线程复制它所需的全部示例代码。如果您能比我更快地理解它,请告诉我。我能够在遇到每个stdout换行符时调用一个线程,但不能优雅地处理在声明的时间窗口内未发生中断的情况:( 希望代码中的注释足以传达我的意图,否则请让我知道,我可以进一步澄清:

import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.Channels;

public class InterruptInput {   
    static BufferedReader in = new BufferedReader(
            new InputStreamReader(
            Channels.newInputStream(
            (new FileInputStream(FileDescriptor.in)).getChannel())));
    boolean TimeUp = false;

    public static void main(String args[]) {
        try {

            System.out.println("Enter lines of input (user ctrl+Z Enter to terminate):");
            System.out.println("(Input thread will be interrupted in 10 sec.)");
            // interrupt input in 10 sec

            String line = in.readLine();
            while ((line.compareTo("") != 0)) {
            System.out.println("Read line:'"+line+"'");
        TimeOut ti = new TimeOut();
        Thread t = new Thread(ti);
        t.start();
        if((line = in.readLine()) != null) {
            t.interrupt();
            }
            }
        System.out.println("outside the while loop");
        } catch (Exception ex) {
            System.out.println(ex.toString()); // printStackTrace();
        }
    }

    public static class TimeOut extends Thread {
        int sleepTime = 10000;
        private volatile Thread threadToInterrupt;    
        public TimeOut() {
            // interrupt thread that creates this TimeOut.
            threadToInterrupt = Thread.currentThread();
            setDaemon(true);
        }

    public void run() {
        System.out.println("starting a new run of the sleep thread!!");
            try {
                sleep(10000); // wait 10 sec
            } catch(InterruptedException ex) {/*ignore*/
        System.out.println("TimeOut thread interrupted!!");
        //I need to exit here if possible w.out executing
            //everything after the catch block
        }
           //only intend to come here when the 10sec wait passes
           //without interruption. Not sure if its plausible

        System.out.println("went through TimeOut without interruption");
        //TimeUp = true;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接