Java程序与其它进程的数据通信
Java程序中可以启动其他的应用程序,这种在Java中启动的进程称为子进程,启动子进程的Java程序称为父进程,其实这个父进程就是一个Java虚拟机
1、在Java程序中可以用Process类的实例对象来表示子进程,子进程的标准输入和输出不再连接到键盘和显示器(也就是不再接收键盘输入,和显示器输
出),而是以管道流的形式连接到父进程的一个输出流和输入流对象上
2、调用Process类的getOutputStream和getInputStream方法可以获得连接到子进程的输出流和输入流对象。子进程从标准输入读取到的内容就是父进程
通过输出流对象写入到它们俩之间的进程管道中的数据,子进程写入的标准输出的数据通过它们之间的进程管道传递到了父进程的输入流对象中,父进程
从这个输入流对象中读取到的内容就是子进程写入到标准输出的数据
编程实例:在TestInOut类中启动java.exe命令执行另外一个MyTest类,TestInOut和MyTest通过进程间的管道相互传递数据
TestInOut启动两个线程,一个不停的向MyTest中发送数据,另一个它不停的读取MyTest写回的数据
import
java.io.*;
class test implements Runnable{
Process p = null;
public
test() throws Exception
{
p = Runtime.getRuntime().exec("java
MyTest"); //启动子进程,这个程序不存在会出现问题!
new Thread(this).start(); //启动线程,用new
test()时,因为在构造函数中,所以就创建了两个实例对象,Thread会调用第二个对象中的run方法,而run方法调用的p就是第二个对象中的成员变量,而send方法所调用的p是第一个test对象中的成员变量,这样两个p不匹配了。还有可以产生无限递归,因为在构造函数中,创建一次就又调用一次构造方法,所以应该用this代替
}
public
static void main(String[] args) throws Exception{
test ts = new test();
//创建对象时,子进程就启动了,接收线程也启动了
ts.send();
}
public void send() throws
Exception{
try{
OutputStream ops = p.getOutputStream();
//发送,首先要连接一个输出流对象
while(true)
{
ops.write("help/r/n".getBytes()); //写入字符串
}
}catch(Exception e){
e.printStackTrace();}
}
public void run(){
try{
InputStream in = p.getInputStream();
//接收,首先要获取输入流对象
BufferedReader bfr = new BufferedReader(new
InputStreamReader(in));
//为了一次读一行,就可以使用BufferedReader这个包装类来包装InputStream类,包装这个类需要先将字节流转换成字符流以后才能包装,查帮助!
while(true){
String strLine = bfr.readLine();
System.out.println(strLine);
}
}catch(Exception e){
e.printStackTrace();}
}
}
import java.io.*;
public class
MyTest{
public static void main(String[] args){
while(true)
{
try{
System.out.println("hi:"+
new
BufferedReader(new InputStreamReader(System.in)).readLine());
//读取父进程得数据
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
【注意】每次执行都会启动一个子进程,在编译器中用Ctrl+c强行终止父进程后,子进程还是在运行。BufferedReader的实现是有点问题的,父进程已经终止了,子进程读取时,BufferedReader应该要抛出异常,让程序结束!根据BufferedReader的JDK帮助,那么我们对子进程代码进行一些修改,这样父进程终止后,子进程也结束了。那么test这个类也可以进行类似的修改,就可以不再不停的打印出null而不终止了。
import
java.io.*;
public class MyTest{
public static void main(String[]
args){
while(true)
{
try{
String strLine =
new BufferedReader(new InputStreamReader(System.in)).readLine();
if(strLine != null)
{
System.out.println("hi:"+
strLine); //读取父进程得数据
}
else{
return;
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
【思考】另外在上面的例子中我们可以看到数据丢失的情况,比如有字符串没打印完全的行,这是因为管道是有一定容量的,这个管道的容量就是PipedInputStream管道输入流缓冲区的大小,如果缓冲区满后,输出程序还在不停的写数据,就可能将前面的几个数据顶出去,那么如果接收端比较慢,那么就有可能少读一些内容回来了!Java对缓冲区满了后,它到底是以什么样的方式来处理的,是将前面的几个数据顶出去,还是将写进程阻塞,还是抛出异常?
验证管道缓冲区满后,将发生下面的哪种情况:
1、新的数据写入时,将最前面写入的数据挤出去,从而发生数据丢失
2、与PipedInputStream相连的PipedOutputStream无法再写入新的数据,PipedOutputStream.write方法处于阻塞状态
3、与PipedInputStream相连的PipedOutputStream无法再写入新的数据,PipedOutputStream.write方法抛出异常
【程序验证】
1、先让子进程不读取数据(MyTest类),空循环,那么父进程在不停的往管道里面写,就肯定会发生阻塞。这时运行程序,程序停滞不前了,不清楚是什么状况,但可以肯定的是缓冲区满了后没有抛出异常。注意,这时系统进程中有子进程,虽然父进程我们用Ctrl+c结束了
2、在父进程的send方法中先打印一个行号,即每次循环时,这样就能知道它是阻塞了还是继续在写,因为做第一步操作时,我们只能看到程序停滞不前,什么信息都没有。这时会发现行号打印到一定的时候程序就停止了(缓冲区跟机器有关?因为每个机器运行后的行号停止位置不一样。我是到了1366),
这样就说明了往管道缓冲区写满了以后,写的进程就发生了阻塞,而不是把前面的数据顶出去。注意,这时系统进程中有子进程,虽然父进程我们用Ctrl+c结束了
验证了管道缓冲区满后将出现的情况,我们接下来解决数据丢失的问题
1、还原代码,让子进程(MyTest类)再继续不停的读数据,注意写进程根据上面的验证应该是没什么问题的,它不会丢失数据
2、观察结果,这个情况应该不是读丢了数据,要是读丢了数据它怎么会将"hi:"这个我们在子进程加入的字符打印出来了呢,既然它都读到了"hi:",那
么它应该没有丢失信息,丢失信息应该是子进程读到的strLine中的内容不是完整的信息
3、现在就可以基本上确定问题发生了MyTest这个类中,而它的有效代码不长,打印内容部分应该没什么问题,那么问题就集中在String
strLine = new
BufferedReader(new
InputStreamReader(System.in)).readLine();这一句上。要解决这一句的问题,一般来说不太好掌握,但是对这种情况下我们自己
分析不出来原因的时候,可以把代码改用其他方式来写。
4、将这一句放到循环外面,因为我们找不出原因,可以将代码分拆试试,换个位置试试,因为这里代码也比较简单。如定义一个变量bfr,然后在循环中
调用readLine方法,即
BufferedReader bfr = new BufferedReader(new
InputStreamReader(System.in));
while(true)
{
String strLine
= bfr.readLine();
}
5、这时发现程序正常了,没有丢失信息了,这是为什么呢?我们将代码移个位置就好了。
6、再回头看看原始的代码,new
BufferedReader(new
InputStreamReader(System.in))在循环中,每循环一次就创建一次新BufferedReader对象。而输
入输出流应该调用它们close方法关闭系统创建的流资源(不是Java创建的对象,而是系统创建的资源)。关闭Java创建的对象后,这个流资源对象不一定
会释放。这样运行的次数多了,积累的这种资源越来越多,系统就出问题了。
7、思考一下这时我们如果就在循环中创建对象,然后调用close方法会怎么样呢即
while(true)
{
BufferedReader bfr = new BufferedReader(new
InputStreamReader(System.in));
String strLine = bfr.readLine();
...
bfr.close()
}
8、再次编译运行,发现编译器只打印了一行信息,然后就报IO异常,管道已结束,这是为什么呢?
9、再分析这个问题,如果关闭流栈中最上层的流对象,它就会关闭这个流栈中所有底层的流,我们调用bfr.close(),那么它会连InputStreamReader和System.in所关联的流资源都给关闭掉。第二次循环时,System.in所关联的那个资源已经被关闭了,这时就不存在了,并且,这个异常时父进程报出来的,而子进程中将System.in一关闭掉,父进程就会返回-1,认为自己结束了,所以System.in就不能轻易的关闭掉
10、所以解决数据丢失的方法就是将BufferedReader
bfr = new BufferedReader(new
InputStreamReader(System.in))放在循环外面。所以要注意程序代码的放置位置。另外我们用Java虚拟机启动一个子进程的时候,我们一定要注意在父进程结束时一定要让子进程结束。记住调用Process类的destroy方法结束子进程的运行(因为这个例子中io正好返回一个null,所以我们用null来判断,一般应该使用destroy方法来结束子进程)。并且在程序中创建子进程的情况很常见,如JCreator它会调用javac.exe和java.ext
【提示】要有一种思想,在编程时,脑中不要想的是一行行的代码,而是一个一个对象,程序每运行一句每个对象在干什么,是不是又多了对象,又少了对象,对象状态又怎么样去变化了,应该这样去思考。
提高程序的运行效率
1、for(int i=0;i<str.length();i++)
{..}
2、int len = str.length();
for(int i=0;i<len;i++)
{..}
第2种方法比第1种效率高,因为第1种每次循环都要调用length方法来检测字符串长度
3、byte[] buf = new byte[1024]
while(true){..}
4、while(true)
{byte[] buf = new
byte[1024];}
第3种方法比第4种效率高,因为第4种每次循环都要创建一次数组对象,并且多占内存
Java程序与其它进程的数据通信的更多相关文章
- Java程序-进程中的"进程"
进程 我们知道程序在磁盘上的时候是静态的,当他被加载到内存的时候,就变成了一个动态的,称为进程,如下图是程序被加载到内存后,在内存中的分布情况如下 此图来自http://blog.csdn. ...
- java程序怎么在一个电脑上只启动一次,只开一个进程
目录 <linux文件锁flock> <NIO文件锁FileLock> <java程序怎么在一个电脑上只启动一次,只开一个进程> 方案1: 单进程程序可以用端口绑定 ...
- eclipse 附加进程调试java程序(转)
转自:http://blog.csdn.net/zhoushenghuang/article/details/54485645 第一步,启动Java程序时需要设置JVM参数 右击java项目-> ...
- java程序在一个电脑上只启动一次,只开一个进程
方案1: 单进程程序可以用端口绑定.程序启动的时候可以尝试看该端口是否已经被占用,如果占用则程序已经启动. 方案2:你可以在java程序中创建一个隐藏文件,程序退出的时候删除这个文件.这样在程序启动的 ...
- Efficient&Elegant:Java程序员入门Cpp
最近项目急需C++ 的知识结构,虽说我有过快速学习很多新语言的经验,但对于C++ 老特工我还需保持敬畏(内容太多),本文会从一个Java程序员的角度,制定高效学习路线快速入门C++ . Java是为了 ...
- Java 程序优化 (读书笔记)
--From : JAVA程序性能优化 (葛一鸣,清华大学出版社,2012/10第一版) 1. java性能调优概述 1.1 性能概述 程序性能: 执行速度,内存分配,启动时间, 负载承受能力. 性能 ...
- 2016-2017-2 《Java程序设计》教学进程
2016-2017-2 <Java程序设计>教学进程 目录 考核方式 课前准备 教学进程 第00周学习任务和要求 第01周学习任务和要求 第02周学习任务和要求 第03周学习任务和要求 第 ...
- Java程序员岗位
Java程序员岗位面试题有哪些? 1.面向对象的特征有哪些方面(1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择 ...
- 如何利用 JConsole观察分析Java程序的运行,进行排错调优
原文链接:http://jiajun.iteye.com/blog/810150 一.JConsole是什么 从Java 5开始 引入了 JConsole.JConsole 是一个内置 Java 性能 ...
随机推荐
- Clausen Functions (and related series, functions, integrals)
Since the Clausen functions are intimately related to a number of other important special functions, ...
- 原生js按回车键实现登录
这篇文章主要介绍了原生JS按回车键实现登录的方法,众所周知,这是在web程序设计中的一个非常实用的小技巧,主要用于表单提交,包括注册.登录等等功能,具有很好的用户体验,有着非常广泛的实用价值,需要的朋 ...
- centos7一步一步搭建docker tomcat 及重点讲解
系统环境:centos7.7 (VMware中) image版本:tomcat:8-jdk8-openjdk (截止2020.01.10该系列版本) 安装步骤参考文章:https://www.jian ...
- C语言:将3*4矩阵中找出行最大,列最小的那个元素。-将低于平均值的人数作为函数返回值,将低于平均分的分数放入below数组中。
//将3*4矩阵中找出行最大,列最小的那个元素. #include <stdio.h> #define M 3 #define N 4 void fun(int (*a)[N]) { ,j ...
- Halcon blob分析基本处理步骤
Halcon,blob分析 应用场景,二值化后的灰度图像对比度清晰 基本处理流程 1 读取图片 read_image(变量名,'路径') //halcon字符串使用单引号'' 2 预处理 2.1 RO ...
- opencv python:ROI 与 泛洪填充
提取ROI区域,处理然后放回去: 泛洪填充 测试代码:显示一张图像,鼠标点击之后,会从该点开始进行填充,显示填充后的结果图像 注:二值图像的填充需要使用选项:cv2.FLOODFILL_MASK_ON ...
- Jmeter调度器小记
jmeter的调度器中[持续时间(秒)]的优先级是高于[结束时间]和[启动时间]的 举例子: 前提:[循环次数]勾选[永远] 场景1:[持续时间(秒)]设置为120S,[启动时间]设置T+1min,[ ...
- Spring学习(八)
AOP的重要概念 1.切面 : 切点(Pointcut) + Advice[ 在哪里 .加什么 ] 2.Advice: 在 切点 选中的 连接点 "加入" 的 代码 就是 Advi ...
- pytoch 安装
注意替换清华源,否则直接安装速度会很慢. conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud ...
- Update(Stage5):DMP项目_业务介绍_框架搭建
DMP (Data Management Platform) 导读 整个课程的内容大致分为如下两个部分 业务介绍 技术实现 对于业务介绍, 比较困难的是理解广告交易过程中各个参与者是干什么的 对于技术 ...