启动进程的方式说明

  • 通过 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();// endregiondos = 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.EOFExceptionout.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

处理父子进程之间通信出现的中文乱码问题

唯一要点:保证发送方和读取方使用的同一编码。

  • InputStreamReaderOutputStreamWriter 默认使用jvm默认编码读取数据:Charset.defaultCharset().name();

    • 如果父进程和子进程默认编码不一致,就需要手动指定编码
    • 也可以父进程通过 “java” + “-Dfile.encoding=” + Charset.defaultCharset().name() 保证父子进程编码一致。
  • 也可以使用 DataOutputStreamDataInputStream 这种处理了编码的特殊流。
    • 使用这对流时,建议使用 writeUTFreadUTF 方法,并且关闭流前写一个空字符串。

Java启动子进程以及父子进程间通信相关推荐

  1. Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)

    共享存储映射 文件进程间通信 使用文件也可以完成 IPC,理论依据是,fork 后,父子进程共享文件描述符.也就共享打开的文件. 编程:父子进程共享打开的文件.借助文件进行进程间通信. 测试代码 /* ...

  2. mmap父子进程间通信

    父子等有血缘关系的进程之间也可以通过mmap建立的映射区来完成数据通信.但相应的要在创建映射区的时候指定对应的标志位参数flags:MAP_PRIVATE:(私有映射)父子进程各自独占映射区:MAP_ ...

  3. Linux下调用fork或system启动子进程的信号和资源释放相关问题

    最近一段时间,公司的网管系统二期优化需要新增功能,实现对网管客户端程序进行保护的监控脚本的自动更新及保护进程的监控告警.网管客户端程序分为两部分:客户端GatherClient及保护进程gatherc ...

  4. Linux:进程间通信(创建匿名管道,父子进程间通信,兄弟进程间通信)

    过一遍管道的基础知识: 1.本质:存在于高速缓存区,属于伪文件,不占用磁盘空间.缓冲区大小默认4K,可根据实际情况适当调整 2.读端和写端各自对应一个文件描述符,数据从写端流入,读端流出 3.操作管道 ...

  5. 使用管道完成父子进程间通信

    什么是进程间通信         Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间.任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须 ...

  6. python多进程的使用(导包、创建子进程、启动子进程)

    1 导入进程包 #导入进程包 import multiprocessing 2. Process进程类的说明 Process([group [, target [, name [, args [, k ...

  7. Java 接受reactjs数据_[Java教程]react.js 父子组件数据绑定实时通讯

    [Java教程]react.js 父子组件数据绑定实时通讯 0 2017-09-23 17:00:14 import React,{Component} from 'react'import Reac ...

  8. Java启动参数与内存调优一些学习笔记

    转载自  Java启动参数与内存调优一些学习笔记 .参数的含义 -Xms128m JVM初始分配的堆内存 -Xmx512m JVM最大允许分配的堆内存,按需分配 -XX:PermSize=64M JV ...

  9. python打开.data_DataX初体验-python命令启动以及纯java启动

    一.DataX安装 官网描述很详细,很简单---->DataX安装 二.使用示例:从Oracle数据库导数据到Mysql数据库 事先准备: Oracle数据库的ORDER_INFO表,MySQL ...

最新文章

  1. lambda在python中的用法_在python中对lambda使用.assign()方法
  2. Android开发工具
  3. MS Vs.net 2003 Sp1发布!
  4. Tomcat-简易使用教程
  5. 定价相关的主要用户出口
  6. 内建控制结构之if表达式
  7. html5外置样式表,HTML5移动端通用css详解
  8. 又一位…8月,痛失6位院士!
  9. LeetCode for SQL 176. 第二高的薪水 (ifnull limit order by)
  10. sublime text c++ mac 怎么用_Mac 地址是什么,怎么用
  11. P1251 餐巾计划问题 费用流
  12. Golang (Go语言) Mac OS X下环境搭建 环境变量配置 开发工具配置 Sublime Text 2 【转】...
  13. 再读simpledb 之 SQL语句解析(1)
  14. origin2019插入图片_OriginPro2019免费版
  15. Spring boot 集成 Redis Scarch
  16. 联想怎么启动windows无线服务器,Windows7系统下开启无线的多种方法
  17. 无人机编程donekit及通讯(一)
  18. 二叉树的遍历——层序遍历
  19. Matlab的数据导入与拟合
  20. springMVC+WebUploader实现多图片上传

热门文章

  1. 非计算机专业毕业如何从事IT工作
  2. 广告点击率预估中的特征选择
  3. linux 日志空间,情景linux--如何解决服务器日志过多导致的磁盘空间不足?
  4. XTW100高速编程器WIN10驱动安装
  5. 模电学习02:晶体三极管
  6. mybatis-plus中and和or的使用
  7. 工业交换机和普通交换机的区别
  8. linux sdb空间转移,Linux系统sda变sdb的解决
  9. 是什么偷走了我们浓浓的年味?
  10. Connection Timeout 问题排查