网站首页 java综合 正文
启动进程的方式说明
- 通过
new ProcessBuilder(String ...commands).start()启动进程-
ProcessBuilder支持链式编程来配置子进程的相关设置- redirectXXX:重定向子进程的流(标准输入,标准输出,错误信息)
- environment() 获取环境设置,可修改
- 注意:commands 不是单纯的将命令行参数以空格分隔得到。如果 commands 中单个值过长,可能会启动失败。Runtime 中是用的
StringTokenizer解析分割参数为一个 commands 数组。
-
- 通过 Runtime 封装的方法启动进程比如:
- Runtime.getRuntime().exec(xxx)
- 如果是启动 java 程序。并且不是其他 jar 包,可以如下拼接命令行:
String javaHome = System.getProperty("java.home");
String java = javaHome + File.separator + "bin" + File.separator + "java";
String sysCp = System.getProperty("java.class.path");
String currPath = ClassLoader.getSystemResource("").getPath();
String cp = "\\"" + sysCp + File.pathSeparator + currPath + "\\"";
String encoding = " -Dfile.encoding=" + Charset.defaultCharset().name();
String cmd = java + encoding + " -cp " + cp + ChildProcess.class;
- Runtime 解析命令行为 commands 数组的方法:
public static String[] resolveCommand(String command) {
if (command.length() == 0) {
throw new IllegalArgumentException("Empty command");
}
StringTokenizer st = new StringTokenizer(command);
String[] cmdArray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
cmdArray[i] = st.nextToken();
}
return cmdArray;
}
案例:
- 父进程
import cn.hutool.core.thread.ThreadUtil;
import java.io.*;
import java.nio.charset.Charset;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
public class FatherProcess {
public static void main(String[] args) throws IOException {
String javaHome = System.getProperty("java.home");
String java = javaHome + File.separator + "bin" + File.separator + "java";
String sysCp = System.getProperty("java.class.path");
String currPath = ClassLoader.getSystemResource("").getPath();
String cp = "\\"" + sysCp + File.pathSeparator + currPath + "\\"";
// 父进程和子进程如果编码不一致,会出现中文乱码。可以包装流,使得双方编码一致
// 或者父进程启动子进程的时候,设置 " -Dfile.encoding=" + Charset.defaultCharset().name();
String encoding = " -Dfile.encoding=" + Charset.defaultCharset().name();
String cmd = java + encoding + " -cp " + cp + ChildProcess.class;
// Process p = Runtime.getRuntime().exec(cmd);
// 失败,需要用 StringTokenizer 解析命令行为数组,可能单个字符串是太长了
// Process p = new ProcessBuilder(java, "-classpath", cp, ChildProcess.class.toString()).start();
// 可以通过 ProcessBuilder 重定向子进程的流到文件,此时父进程将无法通过 p.getInputStream() 获取子进程输出
ProcessBuilder processBuilder = new ProcessBuilder(resolveCommand(cmd));
processBuilder.redirectOutput(new File("target/output.txt"));
processBuilder.redirectError(new File("target/error.txt"));
Process p = processBuilder.start();
System.out.println("FatherProcess's default charset is: " + Charset.defaultCharset().name());
// 父进程通过 IO 流将信息写入子进程的输入流
System.out.println("【父进程发送两条数据】:" + 2);
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()))) {
// 子进程用的时 readLine,因此需要换行符
writer.write("消灭人类暴政\\n");
writer.flush();
ThreadUtil.sleep(1, TimeUnit.SECONDS);
writer.write("世界属于三体\\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String[] resolveCommand(String command) {
if (command.length() == 0) {
throw new IllegalArgumentException("Empty command");
}
StringTokenizer st = new StringTokenizer(command);
String[] cmdArray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
cmdArray[i] = st.nextToken();
}
return cmdArray;
}
}
- 子进程
import cn.hutool.core.thread.ThreadUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class ChildProcess {
public static void main(String[] args) throws IOException {
System.out.println("ChildProcess's default charset is: " + Charset.defaultCharset().name());
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
String line;
List<String> all = new ArrayList<>();
// readLine 以换行符作为一行结束
while ((line = reader.readLine()) != null) {
all.add(line);
}
// println 带有换行符
System.out.println("【子进程收到的消息数量】:" + all.size());
ThreadUtil.sleep(200, TimeUnit.MILLISECONDS);
System.out.println("给岁月以文明");
ThreadUtil.sleep(200, TimeUnit.MILLISECONDS);
System.out.println("而不是给文明以岁月");
System.out.println(all);
}
}
}
通过IO流进行通信——DataOutputStream 和 DataInputStream
也可以使用 BufferedWriter、BufferedReader。如果使用 readLine 方法,要注意 BufferedWriter 必须写入换行符(或关闭流)后,BufferedReader.readLine() 才能读取到内容。
基于默认IO流的方式:
- 父进程使用 process.getOutputStream 向子进程写入数据,子进程通过 System.in 读取数据
- 父进程使用 process.getInputStream 读取子进程输出的数据。
案例
- 父进程
import cn.hutool.core.util.StrUtil;
import java.io.*;
import java.util.StringTokenizer;
/**
* @author zhy
*/
public class FatherProcessWithDOS {
private final Process process;
private final DataOutputStream dos;
public FatherProcessWithDOS() throws IOException {
String javaHome = System.getProperty("java.home");
String java = javaHome + File.separator + "bin" + File.separator + "java";
String sysCp = System.getProperty("java.class.path");
String currPath = ClassLoader.getSystemResource("").getPath();
String cp = "\\"" + sysCp + File.pathSeparator + currPath + "\\"";
String cmd = java + " -cp " + cp + ChildProcessWithDIS.class;
process = new ProcessBuilder(resolveCommand(cmd)).start();
// region 监听子进程的错误输出
new Thread(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.err.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// endregion
// region 监听子进程的标准输出
new Thread(() -> {
try (DataInputStream dis = new DataInputStream(process.getInputStream())) {
String line;
while (!StrUtil.isEmpty((line = dis.readUTF()))) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// endregion
dos = new DataOutputStream(process.getOutputStream());
}
public void sendToChild(String message) {
try {
dos.writeUTF(message);
// dos.writeBytes(message);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isChildAlive() {
return process.isAlive();
}
public void destroyChild() {
process.destroy();
}
private String[] resolveCommand(String command) {
if (command.length() == 0) {
throw new IllegalArgumentException("Empty command");
}
StringTokenizer st = new StringTokenizer(command);
String[] cmdArray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
cmdArray[i] = st.nextToken();
}
return cmdArray;
}
public static void main(String[] args) throws IOException, InterruptedException {
FatherProcessWithDOS father = new FatherProcessWithDOS();
String message = "消灭人类暴政";
System.out.println("父进程发送数据:" + message);
father.sendToChild(message);
Thread.sleep(100);
message = "世界属于三体";
System.out.println("父进程发送数据:" + message);
father.sendToChild(message);
Thread.sleep(100);
message = "man remember love because romantic only.";
System.out.println("父进程发送数据:" + message);
father.sendToChild(message);
Thread.sleep(100);
message = "exit";
System.out.println("父进程结束命令:" + message);
father.sendToChild(message);
Thread.sleep(100);
System.out.println("子进程是否存活:" + father.isChildAlive());
System.exit(0);
}
}
- 子进程
import cn.hutool.core.util.StrUtil;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author zhy
*/
public class ChildProcessWithDIS {
public static void main(String[] args) {
read(System.in);
}
private static void read(InputStream inputStream) {
DataOutputStream out = new DataOutputStream(System.out);
try (DataInputStream reader = new DataInputStream(inputStream)) {
String line;
while (!StrUtil.isEmpty((line = reader.readUTF()))) {
if ("exit".equalsIgnoreCase(line)) {
// 必须写一个空字符串,不然抛出异常 java.io.EOFException
out.writeUTF("");
return;
}
String dateStr = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
String msg = MessageFormat.format("[{0}][receive a line message]:{1}", dateStr, line);
// System.out.println(msg);
out.writeUTF(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
通过 IO 流 + Socket 进行通信(需要端口,大可不必使用)
- 父进程
import cn.hutool.core.net.NetUtil;
import java.io.*;
import java.net.Socket;
import java.util.StringTokenizer;
/**
* @author zhy
*/
public class FatherProcessWithSocket {
private final Process process;
private final int port;
public FatherProcessWithSocket() throws IOException {
String javaHome = System.getProperty("java.home");
String java = javaHome + File.separator + "bin" + File.separator + "java";
String sysCp = System.getProperty("java.class.path");
String currPath = ClassLoader.getSystemResource("").getPath();
String cp = "\\"" + sysCp + File.pathSeparator + currPath + "\\"";
port = NetUtil.getUsableLocalPort();
String cmd = java + " -cp " + cp + ChildProcessWithSocket.class + " " + port;
process = new ProcessBuilder(resolveCommand(cmd)).start();
// region 监听子进程的错误输出
new Thread(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.err.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// endregion
// region 监听子进程的标准输出
new Thread(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.err.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// endregion
}
public void sendToChild(String message) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new Socket("localhost", port).getOutputStream()))) {
writer.write(message);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isChildAlive() {
return process.isAlive();
}
public void destroyChild() {
process.destroy();
}
private String[] resolveCommand(String command) {
if (command.length() == 0) {
throw new IllegalArgumentException("Empty command");
}
StringTokenizer st = new StringTokenizer(command);
String[] cmdArray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
cmdArray[i] = st.nextToken();
}
return cmdArray;
}
public static void main(String[] args) throws IOException, InterruptedException {
FatherProcessWithSocket father = new FatherProcessWithSocket();
String message = "消灭人类暴政";
System.out.println("父进程发送数据:" + message);
father.sendToChild(message);
Thread.sleep(100);
message = "世界属于三体";
System.out.println("父进程发送数据:" + message);
father.sendToChild(message);
Thread.sleep(100);
message = "exit";
System.out.println("父进程结束命令:" + message);
father.sendToChild(message);
Thread.sleep(100);
System.out.println("子进程是否存活:" + father.isChildAlive());
}
}
- 子进程
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author zhy
*/
public class ChildProcessWithSocket {
public static void main(String[] args) {
if (args.length > 0) {
int port = Integer.parseInt(args[0]);
new Thread(() -> startSocket(port)).start();
}
// read(System.in);
}
private static void startSocket(int port) {
try {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
if (read(inputStream)) {
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static boolean read(InputStream inputStream) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
if ("exit".equalsIgnoreCase(line)) {
return true;
}
String dateStr = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
String msg = MessageFormat.format("[{0}][receive a line message]:{1}", dateStr, line);
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
小结
Java 启动子进程的方式
- 通过
new ProcessBuilder(String ...commands).start()启动进程-
ProcessBuilder支持链式编程来配置子进程的相关设置- redirectXXX:重定向子进程的流(标准输入,标准输出,错误信息)
- environment() 获取环境设置,可修改
- 注意:commands 不是单纯的将命令行参数以空格分隔得到。如果 commands 中单个值过长,可能会启动失败。Runtime 中是用的
StringTokenizer解析分割参数为一个 commands 数组。
-
- 通过 Runtime 封装的方法启动进程比如:
- Runtime.getRuntime().exec(xxx)
- 如果是启动 java 程序。并且不是其他 jar 包,可以如下拼接命令行:
String javaHome = System.getProperty("java.home");
String java = javaHome + File.separator + "bin" + File.separator + "java";
String sysCp = System.getProperty("java.class.path");
String currPath = ClassLoader.getSystemResource("").getPath();
String cp = "\\"" + sysCp + File.pathSeparator + currPath + "\\"";
String encoding = " -Dfile.encoding=" + Charset.defaultCharset().name();
String cmd = java + encoding + " -cp " + cp + ChildProcess.class;
- Runtime 解析命令行为 commands 数组的方法:
public static String[] resolveCommand(String command) {
if (command.length() == 0) {
throw new IllegalArgumentException("Empty command");
}
StringTokenizer st = new StringTokenizer(command);
String[] cmdArray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
cmdArray[i] = st.nextToken();
}
return cmdArray;
}
进程 API
- isAlive():判断是否存活
- destroy():结束进程
通信
基于默认IO流的方式:
- 父进程使用 process.getOutputStream 向子进程写入数据,子进程通过 System.in 读取数据
- 父进程使用 process.getInputStream 读取子进程输出的数据。
其他方式:Socket
处理父子进程之间通信出现的中文乱码问题
唯一要点:保证发送方和读取方使用的同一编码。
-
InputStreamReader与OutputStreamWriter默认使用jvm默认编码读取数据:Charset.defaultCharset().name();- 如果父进程和子进程默认编码不一致,就需要手动指定编码
- 也可以父进程通过 “java” + “-Dfile.encoding=” + Charset.defaultCharset().name() 保证父子进程编码一致。
- 也可以使用
DataOutputStream与DataInputStream这种处理了编码的特殊流。- 使用这对流时,建议使用
writeUTF和readUTF方法,并且关闭流前写一个空字符串。
- 使用这对流时,建议使用
原文链接:https://blog.csdn.net/zhy1379/article/details/122510458
相关推荐
- 2022-09-17 Pandas数据类型转换df.astype()及数据类型查看df.dtypes的使用_python
- 2022-08-30 AndroidStudio编译报错 Connect to repo.maven.apache.org
- 2023-07-28 nrm的安装与配置及问题修复
- 2023-10-14 ORACLE存在就修改 不存在就新增(注意更新和新增语法不同于常规语法)
- 2022-03-16 C# 程序通用结构_C#教程
- 2022-08-17 R语言ggplot2拼图包patchwork安装使用_R语言
- 2022-07-13 Andorid 自定义 View - 自定义属性 - 属性重复导致冲突
- 2022-09-03 详解Docker镜像的基本操作方法_docker
- 栏目分类
- 最近更新
-
- window11 系统安装 yarn
- 超详细win安装深度学习环境2025年最新版(
- Linux 中运行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存储小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基础操作-- 运算符,流程控制 Flo
- 1. Int 和Integer 的区别,Jav
- spring @retryable不生效的一种
- Spring Security之认证信息的处理
- Spring Security之认证过滤器
- Spring Security概述快速入门
- Spring Security之配置体系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置权
- redisson分布式锁中waittime的设
- maven:解决release错误:Artif
- restTemplate使用总结
- Spring Security之安全异常处理
- MybatisPlus优雅实现加密?
- Spring ioc容器与Bean的生命周期。
- 【探索SpringCloud】服务发现-Nac
- Spring Security之基于HttpR
- Redis 底层数据结构-简单动态字符串(SD
- arthas操作spring被代理目标对象命令
- Spring中的单例模式应用详解
- 聊聊消息队列,发送消息的4种方式
- bootspring第三方资源配置管理
- GIT同步修改后的远程分支
