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种每次循环都要创建一次数组对象,并且多占内存
(转自)http://blog.sina.com.cn/s/blog_5dc8c6b10100bq1z.html
相关推荐
在这个场景下,我们讨论的是一个简单的聊天小程序,它利用了Java的Socket库来搭建客户端与服务器之间的桥梁,实现数据的实时交互。下面将详细阐述相关知识点。 1. **Java Socket**: Java的Socket类是TCP/IP协议的...
《基于Java的进程间异步通信系统的设计与实现》探讨的是如何在Java环境中构建一个高效、可靠的进程间通信系统,特别是在分布式应用系统开发中的重要性。进程间通信(IPC)是分布式系统的基础,它决定了系统的性能和...
在跨平台的软件开发中,有时需要Java程序与C++程序进行通信,以便实现特定功能或数据共享。本文将探讨如何使用内存映射文件(Memory Mapped Files)和Java Native Interface(JNI)来解决这个问题。这种方法高效且...
Java进程间的管道通信是一种进程间通信(IPC,Inter-Process Communication)的方式,它允许不同的Java进程之间通过共享的管道进行数据传输。虽然Java标准库不直接支持创建管道文件,但可以借助于操作系统提供的功能...
Java进程间通信(IPC, Inter-Process Communication)是多进程应用程序之间共享数据和协同工作的关键技术。在这个特定的实例中,我们关注的是通过管道(Pipe)实现的IPC,这是Java提供的一种简单而有效的通信机制。...
Java进程间的网络通信是计算机编程中的一个重要概念,它允许运行在不同或相同计算机上的多个Java应用程序之间交换数据。在这个场景中,我们有两个关键文件:EchoServer.java和EchoClient.java,它们分别代表服务器端...
主程序可以是一个简单的Java应用,使用`ProcessBuilder`来启动其他JVM,然后通过读取子进程的输出或检查其状态来监控它们。 在`hboot-src`源代码目录中,可能包含了实现这些功能的Java源码。`bin`目录可能包含编译...
在Java编程语言中,进程通信(Process Communication)是多进程应用程序中的关键概念,它涉及到不同进程间的信息交换和协调。Java提供了多种方式来实现进程间的通信,这些方式可以帮助开发者构建复杂的分布式系统和...
《MFC程序与Java程序之间的数据交换》这篇文章深入探讨了MFC(Microsoft Foundation Classes)程序与Java程序之间实现数据交换的技术细节与应用场景,尤其聚焦于如何利用ODBC(Open Database Connectivity)技术在...
Java模拟操作系统是一个基于Java编程语言实现的微型操作系统模型,它主要涵盖了四个核心领域:内存管理、进程管理、文件管理和进程通信。这样的项目旨在帮助开发者理解操作系统的工作原理,并提供了一个实践平台来...
- 错误处理:确保处理进程间的通信异常,防止数据丢失或程序崩溃。 - 同步和互斥:避免数据竞争,确保数据的一致性。 - 性能优化:合理设计通信结构,减少不必要的数据传输和等待时间。 综上所述,"java源码:...
当Java无法满足某些特定需求时,JNI可以用来调用本地(C/C++)代码,间接实现与本地进程的通信。 11. **并发与多线程** 虽然不是严格意义上的进程通信,但Java的并发库(如`java.util.concurrent`包)在多线程...
Socket提供了一种基于TCP/IP协议的进程间通信(IPC)方式,可以实现在不同操作系统上的进程之间的数据传输。在这个场景中,Java作为客户端发起连接请求,而C++作为服务端接收并处理请求。 首先,我们来看Java客户端...
### Java程序多进程运行模式的实例分析 #### 一、多进程的概念与优势 在Java中,多进程是指在一个程序中启动多个独立的Java虚拟机(JVM)实例,每个实例都可以独立运行自己的任务。这种方式相比于单进程具有以下优势...
本示例聚焦于Node.js与Java之间的Socket通信,这是一个非常实用的技术,因为这两种技术在Web开发和后端服务中都占据着重要的地位。让我们详细探讨一下如何实现Node.js与Java的Socket通信。 首先,Socket是一种基于...
在Android开发中,有时我们需要进行Java层与C/C++(JNI)层的通信,以便利用C/C++的高性能优势处理一些底层任务。本教程将详细解释如何在Android中实现Java和C的Socket通信,特别是在涉及文件描述符传递的场景下。 ...
当一个程序被运行时,操作系统会为它创建一个进程,每个进程都有独立的内存空间,包含代码、数据、堆栈等资源。 3. **线程**:线程是进程内的一个执行单元,是CPU调度和执行的基本单位。在一个进程中可以有多个线程...
本资料包"基于Java的进程通信.zip"可能包含了关于Java进程通信的示例代码、教程文档和其他相关资源,以帮助理解这一主题。 首先,我们需要了解什么是进程。在操作系统中,进程是程序的一次执行实例,每个进程都有...
### 用Java实现基于TCP/IP协议的网络通信程序 #### 概述 随着互联网技术的快速发展,现代程序设计越来越依赖于网络通信能力。Java作为一种广泛使用的编程语言,提供了丰富的API来支持网络编程,使得开发者无需深入...