在Java 6中实现`Process.waitFor(long timeout,TimeUnit unit)`

时间:2018-12-03 10:05:49

标签: processbuilder java-6

我正在处理一个旧版(Java 6/7)项目,该项目使用ProcessBuilder以与操作系统无关的方式从计算机请求UUID。我想使用Java 8中的Process.waitFor(long timeout, TimeUnit unit)方法,但是Java 6中没有实现。相反,我可以使用waitFor(),该方法会阻塞直到完成或出现错误。

如果可能的话,我想避免将Java版本升级到8,因为这需要进行许多其他更改(例如,从已删除的内部API迁移代码并升级生产Tomcat服务器)。

我如何最好地实现代码以执行带有超时的过程?我正在考虑以某种方式实施一个计划,以检查该进程是否仍在运行,并取消/销毁该进程,以及是否已达到超时。

我当前的(Java 8)代码如下:

/** USE WMIC on Windows */
private static String getSystemProductUUID() {
    String uuid = null;
    String line;

    List<String> cmd = new ArrayList<String>() {{
      add("WMIC.exe"); add("csproduct"); add("get"); add("UUID");
    }};

    BufferedReader br = null;
    Process p = null;
    SimpleLogger.debug("Attempting to retrieve Windows System UUID through WMIC ...");
    try {
      ProcessBuilder pb = new ProcessBuilder().directory(getExecDir());
      p = pb.command(cmd).start();

      if (!p.waitFor(TIMEOUT, SECONDS)) { // No timeout in Java 6
        throw new IOException("Timeout reached while waiting for UUID from WMIC!");
      }
      br = new BufferedReader(new InputStreamReader(p.getInputStream()));
      while ((line = br.readLine()) != null) {
        if (null != line) {
          line = line.replace("\t", "").replace(" ", "");
          if (!line.isEmpty() && !line.equalsIgnoreCase("UUID")) {
            uuid = line.replace("-", "");
          }
        }
      }
    } catch (IOException | InterruptedException ex) {
      uuid = null;
      SimpleLogger.error(
        "Failed to retrieve machine UUID from WMIC!" + SimpleLogger.getPrependedStackTrace(ex)
      );
      // ex.printStackTrace(System.err);
    } finally {
      if (null != br) {
        try {
          br.close();
        } catch (IOException ex) {
          SimpleLogger.warn(
            "Failed to close buffered reader while retrieving machine UUID!"
          );
        }
        if (null != p) {
          p.destroy();
        }
      }
    }

    return uuid;
  }

1 个答案:

答案 0 :(得分:2)

您可以使用以下代码,该代码仅使用Java 6中可用的功能:

public static boolean waitFor(Process p, long t, TimeUnit u) {
    ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
    final AtomicReference<Thread> me = new AtomicReference<Thread>(Thread.currentThread());
    ScheduledFuture<?> f = ses.schedule(new Runnable() {
        @Override public void run() {
            Thread t = me.getAndSet(null);
            if(t != null) {
                t.interrupt();
                me.set(t);
            }
        }
    }, t, u);
    try {
        p.waitFor();
        return true;
    }
    catch(InterruptedException ex) {
        return false;
    }
    finally {
        f.cancel(true);
        ses.shutdown();
        // ensure that the caller doesn't get a spurious interrupt in case of bad timing
        while(!me.compareAndSet(Thread.currentThread(), null)) Thread.yield();
        Thread.interrupted();
    }
}

请注意,与其他解决方案不同,该解决方案将在调用者线程内执行Process.waitFor()调用,这是使用监视工具查看应用程序时所期望的。这也有助于短期运行子进程的性能,因为调用方线程所做的工作不会比Process.waitFor()多得多,即不需要等待后台线程的完成。而是在后台thead中发生的事情是,如果超时,则启动线程将中断。