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种每次循环都要创建一次数组对象,并且多占内存

时间: 2024-08-24 23:02:42

Java程序与其它进程的数据通信的相关文章

Java程序-进程中的进程

进程 我们知道程序在磁盘上的时候是静态的,当他被加载到内存的时候,就变成了一个动态的,称为进程,如下图是程序被加载到内存后,在内存中的分布情况如下 此图来自http://blog.csdn.net/woshinia/article/details/41722085 具体每段的作用可以参考这篇文章,这里我们只要大概了解一下,在程序被加载到内存后,会被分为代码段,数据段,堆段和栈段.其中代码段就是存放数据,数据段存放一些全局和静态数据,堆段存放的动态创建的内存对象,而栈段则是线程栈运行的区域. Ja

java程序在一个电脑上只启动一次,只开一个进程

方案1: 单进程程序可以用端口绑定.程序启动的时候可以尝试看该端口是否已经被占用,如果占用则程序已经启动. 方案2:你可以在java程序中创建一个隐藏文件,程序退出的时候删除这个文件.这样在程序启动的时候,你判断是否存在这个文件,如果存在说明已经启动. import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileLock; /** *

普通Java程序员学习使用的6个JDK内建工具

与你的问题不同,我认为软件工程主要是用来解决问题的.有些博客认为“每个小孩都应该学习编程”,“你认为学数学只是玩玩而已?如果你有看过我的HTML5调试器的话,你会发现我是一个程序员,但我做的工作远不止数学这些”. 上面两者都同意一个观点,软件工程不只是用计算机语言写的一些只言片语.软件解决的问题诠释了程序员的价值. 解决问题的最终进展来自科学.强化清晰的头脑和我们一路以来使用的工具. 你有没有留意过那些 JDK 安装附带的工具?既然那些大牛同意把那些工具加到 JDK 里,应该是有用的. 因此,在

JTI + JNI,为Java程序提供获取JVM内部信息的通道

首先,JTI是啥? HotSpot JVM是使用C++写的,在操作系统层面来看,java.exe进程与其他进程并无特别之处.任何一个进程都可以加载第三方的DLL,JTI就是java.exe开放出来的向Java.exe进程注入dll的接口.也就是说,开发者根据JTI定义好的规范,用C++写一个dll,这个dll就可以被java.exe进程加载了[启动jvm的时候要加上-agentlib参数]. JTI的详细资料参见以下网址:http://docs.oracle.com/javase/7/docs/

Java程序员面试题集(1-50)

下面的内容是对网上原有的Java面试题集及答案进行了全面修订之后给出的负责任的题目和答案,原来的题目中有很多重复题目和无价值的题目,还有不少的参考答案也是错误的,修改后的Java面试题集参照了JDK最新版本,去掉了EJB 2.x等无用内容,补充了数据结构和算法相关的题目.经典面试编程题.大型网站技术架构.操作系统.数据库.软件测试.设计模式.UML等内容,同时还对很多知识点进行了深入的剖析,例如hashCode方法的设计.垃圾收集的堆和代.Java新的并发编程.NIO.2等,相信对准备入职的Ja

Java并发编程:进程和线程

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

不错的linux下通用的java程序启动脚本(转载)

转自:http://www.cnblogs.com/langtianya/p/4164151.html 虽然写起动shell的频率非常不高...但是每次要写都要对付一大堆的jar文件路径,新加jar包也必须要修改起动shell. 在网上找到一个挺好的通用shell脚本. 只需要修改一些配置变量,就可以用来做起动脚本了. 并且除了能起动.还支持关闭.重启.查看是否正在运行的功能. 原文地址:http://www.tudaxia.com/archives/10 start函数中,nohup部分其实也

Java程序员转Android开发必读经验

小编最近几日偷偷的发现部分Java程序员想转安卓开发,故此加紧补充知识,为大家搜集资料,积极整理前人的经验,希望可以给正处于困惑中的你,带来些许的帮助. 啰哩啰嗦的说说Java和Android程序的区别: Android是主流智能手机的操作系统,Java是一种开发语言,两者没有好坏优劣之分,只是两种职业岗位的选择.学安卓从事移动互联方向开发,学Java从事软件.网站开发.而安卓上的应用大多是Java编写的,所以建议在安卓前期的Java学习阶段中,要用心学好. 言简意赅的说说“转”前的准备: 其实

Java程序员转Android开发必读经验分享

摘要:DevStore小编最近几日偷偷的发现部分Java程序员想转安卓开发,故此加紧补充知识,为大家搜集资料,积极整理前人的经验,希望可以给正处于困惑中的你,带来些许的帮助. 啰哩啰嗦的说说Java和Android程序的区别: Android是主流智能手机的操作系统,Java是一种开发语言,两者没有好坏优劣之分,只是两种职业岗位的选择.学安卓从事移动互联方向开发,学Java从事软件.网站开发.而安卓上的应用大多是Java编写的,所以建议在安卓前期的Java学习阶段中,要用心学好. 言简意赅的说说