如何销毁以构造函数参数传入的Process进程?iperf服务端超时场景实践疑问
问题分析与解决方案
首先咱们得搞清楚为什么会抛出NullPointerException——这是因为Java的参数传递是值传递,哪怕是对象引用也是按值传递的。你在main里初始化的Process p = null,传给Task构造函数时,Task里的p是一个全新的引用变量,和main里的p完全没关系。当你在Task.call()里给this.p赋值创建好的iperf进程时,main里的p依然是null,所以超时后调用p.destroyForcibly()肯定会炸。
要解决这个问题,核心是让main线程能拿到Task里创建的Process对象。我们可以用一个可共享的容器类来包装Process,让main和Task操作同一个容器里的进程引用。这里推荐用AtomicReference,它是线程安全的,非常适合这种多线程共享对象的场景。
修改后的代码示例
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; public class Main { public static void main(String[] args) throws IOException { // 用AtomicReference包装Process,实现线程间共享 AtomicReference<Process> processRef = new AtomicReference<>(); ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.submit(new Task(processRef)); try { System.out.println("Started.."); String result = future.get(5, TimeUnit.SECONDS); System.out.println("Finished!"); System.out.println(result); } catch (TimeoutException | InterruptedException | ExecutionException e) { // 尝试获取并销毁进程 Process p = processRef.get(); if (p != null) { p.destroyForcibly(); System.out.println("iperf进程已强制终止"); } future.cancel(true); System.out.println("Terminated!"); } finally { // 确保线程池关闭 executor.shutdownNow(); } } } class Task implements Callable<String> { private final AtomicReference<Process> processRef; public Task(AtomicReference<Process> processRef) { this.processRef = processRef; } @Override public String call() throws Exception { Process p = null; StringBuilder toDisplay = new StringBuilder(); try { p = Runtime.getRuntime().exec("iperf3.exe -s -1"); // 将创建好的进程存入共享容器 processRef.set(p); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); String s; // 读取输出时检查线程是否被中断,及时响应取消操作 while ((s = br.readLine()) != null && !Thread.currentThread().isInterrupted()) { toDisplay.append(s).append("\n"); } // 正常结束时销毁进程 if (p != null) { p.destroyForcibly(); } return toDisplay.toString(); } catch (IOException e) { // 异常时也要清理进程 if (p != null) { p.destroyForcibly(); } throw e; } } }
关键优化点说明
- 用
AtomicReference共享进程引用:让main和Task能访问到同一个Process对象,解决了值传递导致的引用不共享问题。 - 线程中断检查:在读取进程输出的循环里加入
!Thread.currentThread().isInterrupted()判断,当future.cancel(true)触发线程中断时,能及时跳出循环并销毁进程。 - 资源清理:用
try-catch确保无论正常结束还是异常抛出,都能销毁Process,避免僵尸进程。 - finally块关闭线程池:确保线程池一定会被关闭,避免资源泄漏。
这样修改后,超时场景下main线程就能正常拿到并销毁iperf进程,不会再出现空指针异常啦。
内容的提问来源于stack exchange,提问作者Grimalkin
相关产品推荐
相关产品推荐

