代码之家  ›  专栏  ›  技术社区  ›  Agi Hammerthief

在Java 6中实现进程“等待”(长超时,时间单位)

  •  1
  • Agi Hammerthief  · 技术社区  · 6 年前

    我正在研究一个遗留的(Java 6/7)项目,它使用 ProcessBuilder 以操作系统不可知的方式从机器请求uuid。我想用 Process.waitFor(long timeout, TimeUnit unit) 方法从 Java 8 ,但这不是在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 回复  |  直到 6 年前
        1
  •  2
  •   Holger    6 年前

    您可以使用以下代码,这些代码只使用java6提供的功能:

    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() 在调用者线程中调用,这是使用监视工具查看应用程序时所期望的。它也有助于短时间运行的子进程的性能,因为调用方线程不会比 进程.waitfor() ,即不需要等待后台线程完成。相反,在后台thead中发生的是,如果超时时间已过,初始化线程将被中断。