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程序与其它进程的数据通信的更多相关文章

  1. Java程序-进程中的"进程"

    进程 我们知道程序在磁盘上的时候是静态的,当他被加载到内存的时候,就变成了一个动态的,称为进程,如下图是程序被加载到内存后,在内存中的分布情况如下      此图来自http://blog.csdn. ...

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

    目录 <linux文件锁flock> <NIO文件锁FileLock> <java程序怎么在一个电脑上只启动一次,只开一个进程> 方案1: 单进程程序可以用端口绑定 ...

  3. eclipse 附加进程调试java程序(转)

    转自:http://blog.csdn.net/zhoushenghuang/article/details/54485645 第一步,启动Java程序时需要设置JVM参数 右击java项目-> ...

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

    方案1: 单进程程序可以用端口绑定.程序启动的时候可以尝试看该端口是否已经被占用,如果占用则程序已经启动. 方案2:你可以在java程序中创建一个隐藏文件,程序退出的时候删除这个文件.这样在程序启动的 ...

  5. Efficient&Elegant:Java程序员入门Cpp

    最近项目急需C++ 的知识结构,虽说我有过快速学习很多新语言的经验,但对于C++ 老特工我还需保持敬畏(内容太多),本文会从一个Java程序员的角度,制定高效学习路线快速入门C++ . Java是为了 ...

  6. Java 程序优化 (读书笔记)

    --From : JAVA程序性能优化 (葛一鸣,清华大学出版社,2012/10第一版) 1. java性能调优概述 1.1 性能概述 程序性能: 执行速度,内存分配,启动时间, 负载承受能力. 性能 ...

  7. 2016-2017-2 《Java程序设计》教学进程

    2016-2017-2 <Java程序设计>教学进程 目录 考核方式 课前准备 教学进程 第00周学习任务和要求 第01周学习任务和要求 第02周学习任务和要求 第03周学习任务和要求 第 ...

  8. Java程序员岗位

    Java程序员岗位面试题有哪些?   1.面向对象的特征有哪些方面(1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择 ...

  9. 如何利用 JConsole观察分析Java程序的运行,进行排错调优

    原文链接:http://jiajun.iteye.com/blog/810150 一.JConsole是什么 从Java 5开始 引入了 JConsole.JConsole 是一个内置 Java 性能 ...

随机推荐

  1. MyBatis 传一个类型为String的参数时常见问题及解决方法

    MyBatis要求如果参数为String的话,不管接口方法的形参是什么,在Mapper.xml中引用时需要改变为_parameter才能识别 : <select id="selectB ...

  2. 如何在CentOS 7上安装Apache

    使用systemctl管理Apache服务 我们可以像任何其他系统单元一样管理Apache服务. 要停止Apache服务,请运行: sudo systemctl stop httpd 要再次启动,请键 ...

  3. ASP.NET Core搭建多层网站架构【8.2-使用AutoMapper映射实体对象】

    2020/01/29, ASP.NET Core 3.1, VS2019, AutoMapper.Extensions.Microsoft.DependencyInjection 7.0.0 摘要:基 ...

  4. 「CF1051F」The Shortest Statement

    传送门 Luogu 解题思路 注意到 \(m - n \le 20\) ,所以这其实是一个树上问题,非树边至多只有21条,那么我们就可以暴力地对每一个非树边所连接的点求一次单源最短路,然后每次询问时, ...

  5. 《JavaScript高级程序设计》读书笔记(四)变量、作用域和内存问题

    内容---理解基本类型和引用类型的值---理解执行环境---理解垃圾收集 --JavaScript变量松散类型的本质--决定了它只是在特定时间用于保存特定值的一个名字而已--变量的值及其数据类型可以在 ...

  6. Java JDBC 数据库链接小结随笔

    Java JDBC 数据库链接小结随笔 一.链接数据库的步骤 二.关于Statement  和  PrepareStatement 两者区别 用法 三.关于 ResultSet 的一些小结 四.自定义 ...

  7. VBA 学习笔记 - 消息框

    学习资料:https://www.yiibai.com/vba/vba_macro_comments.html 注释 单引号或 REM 开头 丸子:多行注释咋办? 消息框(MsgBox) 函数功能:显 ...

  8. 吴裕雄 python 神经网络——TensorFlow训练神经网络:MNIST最佳实践

    import os import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data INPUT_N ...

  9. 控制面保护(CPPr)

    除了CoPP外,管理员还可以通过使用控制面保护(Control Plane Protection,CPPr)机制抵御针对控制面的攻击,从本质上讲,CPPr属于CoPP的扩展,在CPPr中控制面接口被划 ...

  10. Java 1.8 红黑树

    红黑树 R-B Tree R-B Tree,全称 Red-Black Tree 又称为 红黑树,它是一种特殊的二叉查找树,红黑树的每个节点都有存储位表示节点的颜色,可以是红Red 或者 黑Black ...