`
polim
  • 浏览: 105742 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java 进程通信

    博客分类:
  • Java
 
阅读更多
进程间通信的主要方法有:
(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
(3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。Linux中可以使用kill -12 进程号,像当前进程发送信号,但前提是发送信号的进程要注册该信号。
example:
OperateSignal operateSignalHandler = new OperateSignal();
Signal sig = new Signal("USR2");
Signal.handle(sig, operateSignalHandler);
(4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺限。
(5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
(6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
Java 中有类 MappedByteBuffer实现内存映射
(7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
(8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。


管道方式

一、Java 启动子进程方式
1、
Runtime rt = Runtime.getRuntime();
Process process = rt.exec("java com.test.process.T3");
2、
ProcessBuilder pb = new ProcessBuilder("java", "com.test.process.T3");
Process p = pb.start();

二、Java父、子进程通信方式(管道方式)
父进程获取子进程输出流方式
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String s;
while ((s = br.readLine()) != null) {
  System.out.println(s);
}

子进程获取父进程输入流方式
package com.test.process;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class T3 {

public static void main(String[] args) throws IOException {
System.out.println("子进程被调用成功!");

BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));

while (true) {
String strLine = bfr.readLine();
if (strLine != null) {
System.out.println("hi:" + strLine);
}
}
}

}

三、详细测试类
父进程测试类:
package com.test.process.pipe;
import java.io.IOException;

public class ProcessTest {

public static void main(String[] args) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest");

StringBuilder sbuilder = new StringBuilder();
for(int k=0;k<1;k++){
sbuilder.append("hello");
}

int outSize = 1;
TestOut out[] = new TestOut[outSize];
for(int i=0;i<outSize;i++){
out[i] = new TestOut(p,sbuilder.toString().getBytes());
new Thread(out[i]).start();
}

int inSize = 1;
TestIn in[] = new TestIn[inSize];
for(int j=0;j<inSize;j++){
in[j] = new TestIn(p);
new Thread(in[j]).start();
}
}
}

子进程类
package com.test.process.pipe;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class MyTest {
public static void main(String[] args) throws Exception {
//读取父进程输入流
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String strLine = bfr.readLine();
if (strLine != null) {
System.out.println(strLine);//这个地方的输出在子进程控制台是无法输出的,只可以在父进程获取子进程的输出
}else {
return;
}
}
}
}

TestIn类

package com.test.process.pipe;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class TestIn implements Runnable{

private Process p = null;
public TestIn(Process process){
p = process;
}

@Override
public void run() {
try {
InputStream in = p.getInputStream();
BufferedReader bfr = new BufferedReader(new InputStreamReader(in));
String rd = bfr.readLine();
if(rd != null){
System.out.println(rd);//输出子进程返回信息(即子进程中的System.out.println()内容)
}else{
return;
}
//注意这个地方,如果关闭流则子进程的返回信息无法获取,如果不关闭只有当子进程返回字节为8192时才返回,为什么是8192下面说明.
//bfr.close();
//in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}


TestOut类
package com.test.process.pipe;
import java.io.IOException;
import java.io.OutputStream;

public class TestOut implements Runnable {

private Process p = null;
private byte []b = null;

public TestOut(Process process,byte byt[]){
p = process;
b = byt;
}

@Override
public void run() {
try {
OutputStream ops = p.getOutputStream();
//System.out.println("out--"+b.length);
ops.write(b);
//注意这个地方如果关闭,则父进程只可以给子进程发送一次信息,如果这个地方开启close()则父进程给子进程不管发送大小多大的数据,子进程都可以返回
//如果这个地方close()不开启,则父进程给子进程发送数据累加到8192子进程才返回。
//ops.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


备注:
1、子进程的输出内容是无法在控制台输出的,只能再父类中获取并输出。
2、父进程往子进程写内容时如果关闭字节流,则子进程的输入流同时关闭。
3、如果父进程中输入、输出流都不关闭,子进程获取的字节流在达到8129byte时才返回。
4、关闭子进程一定要在父进程中关闭 p.destroy()


实例1:
/**
*如下另一种情况说明
*如果像如下情况执行会出现说明情况呢
*前提说明:TestOut类中开启ops.close();
*/
package com.test.process.pipe;
import java.io.IOException;

public class ProcessTest {

public static void main(String[] args) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest");

TestOut out = new TestOut(p,"Hello everyone".getBytes());
new Thread(out).start();

TestIn ti = new TestIn(p);
new Thread(ti).start();

Thread.sleep(3000);

TestOut out2 = new TestOut(p,"-Hello-everyone".getBytes());
new Thread(out2).start();

TestIn ti2 = new TestIn(p);
new Thread(ti2).start();
}
}


执行后输出结果为:
Hello everyone
java.io.IOException: Stream closed
        at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:145
)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:308)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
        at java.io.InputStreamReader.read(InputStreamReader.java:167)
        at java.io.BufferedReader.fill(BufferedReader.java:136)
        at java.io.BufferedReader.readLine(BufferedReader.java:299)
        at java.io.BufferedReader.readLine(BufferedReader.java:362)
        at com.test.process.pipe.TestIn.run(TestIn.java:20)
        at java.lang.Thread.run(Thread.java:662)

由此可见当创建一个子进程后,p.getOutputStream();p.getInputStream();通过两种方式使父进程与子进程建立管道连接,而当close()连接时管道关闭,在通过调用
p.getOutputStream();p.getInputStream();时直接出现IOException,结论为当父子进程建立连接后,通过管道长连接的方式进程信息传输,当close时在通过获取子进程的输入输出流
都会出现IOException



实例2:
在实例1的基础上进行修改,会出现什么结果呢,如下

package com.test.process.pipe;
import java.io.IOException;

public class ProcessTest {

public static void main(String[] args) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest");

TestOut out = new TestOut(p,"Hello everyone".getBytes());
new Thread(out).start();

TestIn ti = new TestIn(p);
new Thread(ti).start();

Process p2 = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest");
TestOut out2 = new TestOut(p2,"-Hello-everyone".getBytes());
new Thread(out2).start();

TestIn ti2 = new TestIn(p2);
new Thread(ti2).start();
}
}


输出结果:
Hello everyone
-Hello-everyone

综上可见每个父进程创建一个子进程后,通过p.getOutputStream();p.getInputStream();建立管道连接后,无法关闭流,如果关闭了则需要重新建立进程才可以达到通信的效果。
如果不关闭流,则传输的字符内容累加到8192byte时才可以返回。

为什么是8192byte呢?

JDK 源码分析

Process p = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest");

public Process exec(String command) throws IOException {
return exec(command, null, null);
}

public Process exec(String command, String[] envp, File dir)
        throws IOException {
        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 exec(cmdarray, envp, dir);
    }
   
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
    .environment(envp)
    .directory(dir)
    .start();
    }

接下来会执行 ProcessBuilder.start   

return ProcessImpl.start(cmdarray,environment,dir,redirectErrorStream);

执行ProcessImpl.start(final class ProcessImpl extends Process )
OutputStream
InputStream 是在这里声明的
如下:

//关键这个地方 创建的为FileDescriptor 管理的方式底层也是通过文件的方式实现的,原理跟linux的管道相同
stdin_fd  = new FileDescriptor();
stdout_fd = new FileDescriptor();
stderr_fd = new FileDescriptor();

handle = create(cmdstr, envblock, path, redirectErrorStream,
stdin_fd, stdout_fd, stderr_fd);

java.security.AccessController.doPrivileged(
    new java.security.PrivilegedAction() {
    public Object run() {
stdin_stream =
    new BufferedOutputStream(new FileOutputStream(stdin_fd));
stdout_stream =
    new BufferedInputStream(new FileInputStream(stdout_fd));
stderr_stream =
    new FileInputStream(stderr_fd);
return null;
    }
});
}

Process类中的说明
public abstract class Process
{
    /**
     * Gets the output stream of the subprocess.
     * Output to the stream is piped into the standard input stream of
     * the process represented by this <code>Process</code> object.
     * <p> //该处说明OutputStream 是通过管道的方式进行的处理
     * Implementation note: It is a good idea for the output stream to
     * be buffered.
     *
     * @return  the output stream connected to the normal input of the
     *          subprocess.
     */
    abstract public OutputStream getOutputStream()
}   

BufferedReader类中
private static int defaultCharBufferSize = 8192;//默认字符数组长度

另外Java中还提供了PipedInputStream、PipedOutputStream类,但这2个类用在多进程间交互是无法实现的。


总结:
1、如果Java中要涉及到多进程之间交互,子进程只是简单的做一些功能处理的话建议使用
Process p = Runtime.getRuntime().exec("java ****类名");
p.getOutputStream()
p.getInputStream() 的方式进行输入、输出流的方式进行通信
如果涉及到大量的数据需要在父子进程之间交互不建议使用该方式,该方式子类中所有的System都会返回到父类中,另该方式不太适合大并发多线程
2、内存共享(MappedByteBuffer)
该方法可以使用父子进程之间通信,但在高并发往内存内写数据、读数据时需要对文件内存进行锁机制,不然会出现读写内容混乱和不一致性,Java里面提供了文件锁FileLock,但这个在父/子进程中锁定后另一进程会一直等待,效率确实不够高。
RandomAccessFile raf = new RandomAccessFile("D:/a.txt", "rw");
FileChannel fc = raf.getChannel(); 
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
FileLock fl = fc.lock();//文件锁
3、Socket 这个方式可以实现,需要在父子进程间进行socket通信
4、队列机制 这种方式也可以实现,需要父/子进程往队列里面写数据,子/父进程进行读取。不太好的地方是需要在父子进程之间加一层队列实现,队列实现有ActiveMQ,FQueue等
5、通过JNI方式,父/子进程通过JNI对共享进程读写
6、基于信号方式,但该方式只能在执行kill -12或者在cmd下执行ctrl+c 才会触发信息发生。
OperateSignal operateSignalHandler = new OperateSignal();
Signal sig = new Signal("SEGV");//SEGV 这个linux和window不同
Signal.handle(sig, operateSignalHandler);

public class OperateSignal implements SignalHandler{
@Override
public void handle(Signal arg0) {
System.out.println("信号接收");
}
}
7、要是在线程间也可以使用Semaphore
8、说明一下Java中没有命名管道
分享到:
评论

相关推荐

    java 进程通信 实例(直接运行)

    本人写了一个java进程通信的工具jar,方便大家简单的使用java进程通信。 开启守护进程,获取消息: package com.test; import org.msun.process.ProcessMonitor; import org.msun.process.ProcessMonitor....

    java进程通信.zip

    本资料包“java进程通信.zip”可能包含详细讲解Java进程通信的文档或代码示例。 在Java中,主要的进程通信方法有以下几种: 1. **管道(Pipes)**:Java通过`java.io.PipedInputStream`和`java.io....

    java 进程通信 内存共享 文件加锁.doc

    java 进程通信 内存共享 文件加锁

    基于Java的进程通信.zip

    本资料包"基于Java的进程通信.zip"可能包含了关于Java进程通信的示例代码、教程文档和其他相关资源,以帮助理解这一主题。 首先,我们需要了解什么是进程。在操作系统中,进程是程序的一次执行实例,每个进程都有...

    Java NIO 应用使用内存映射文件实现进程间通信

    实现Java进程间通信的关键在于,不同进程将同一物理文件的不同部分映射到各自的内存空间,通过修改映射的内存内容,可以实现数据的交换。由于MappedByteBuffer与文件内容同步,当一个进程写入数据后,另一个进程可以...

    java源码:进程通信.rar

    综上所述,"java源码:进程通信.rar"这个压缩包可能包含了一系列Java进程通信的实例代码,通过学习这些示例,开发者可以更好地理解和掌握Java中不同进程间通信的原理和实现方法,从而在实际项目中灵活运用。

    java进程间通信机制.ppt

    java进程间通信机制.ppt

    java进程间管道通信1

    Java进程间的管道通信是一种进程间通信(IPC,Inter-Process Communication)的方式,它允许不同的Java进程之间通过共享的管道进行数据传输。...通过理解这些概念和注意事项,开发者可以构建出可靠的多进程通信系统。

    基于Java的实例源码-进程通信.zip

    1. **Java进程通信基础** 在Java中,进程通信通常涉及到两个主要方面:共享内存和消息传递。共享内存允许不同进程通过共享数据区域进行通信,而消息传递则依赖于消息的发送和接收。Java的`java.lang.ProcessBuilder...

    实现nodejs与java的socket相互通信

    本示例聚焦于Node.js与Java之间的Socket通信,这是一个非常实用的技术,因为这两种技术在Web开发和后端服务中都占据着重要的地位。让我们详细探讨一下如何实现Node.js与Java的Socket通信。 首先,Socket是一种基于...

    基于java的进程间异步通信系统的设计与实现.pdf

    《基于Java的进程间异步通信系统的设计与实现》探讨的是如何在Java环境中构建一个高效、可靠的进程间通信系统,特别是在分布式应用系统开发中的重要性。进程间通信(IPC)是分布式系统的基础,它决定了系统的性能和...

    解决Java 和 C++ 程序之间通讯的方法(有图例,含源码)

    在跨平台的软件开发中,有时需要Java程序与C++程序进行通信,以便实现特定功能或数据共享。本文将探讨如何使用内存映射文件(Memory Mapped Files)和Java Native Interface(JNI)来解决这个问题。这种方法高效且...

    java 进程间利用管道通信实例

    Java进程间通信(IPC, Inter-Process Communication)是多进程应用程序之间共享数据和协同工作的关键技术。在这个特定的实例中,我们关注的是通过管道(Pipe)实现的IPC,这是Java提供的一种简单而有效的通信机制。...

    linux下C和java通过管道通信

    中间人程序可以使用`socketpair()`或`socket()`函数创建套接字对,然后分别与C和Java进程通信。 请注意,管道的容量有限,通常为64KB,因此不适合大量数据传输。此外,管道是无名的,即它们没有文件系统路径,只能...

    java 进程之间的网络通信

    Java进程间的网络通信是计算机编程中的一个重要概念,它允许运行在不同或相同计算机上的多个Java应用程序之间交换数据。在这个场景中,我们有两个关键文件:EchoServer.java和EchoClient.java,它们分别代表服务器端...

    JAVA源码进程通信JAVA源码进程通信

    JAVA源码进程通信JAVA源码进程通信

    操作系统进程通信Java源码

    3. **命名管道(Named Pipes)**:在Java中,通常使用`java.nio.channels.Pipe`类来创建命名管道,相比匿名管道,它提供了一种跨进程通信的方式,但Java对命名管道的支持不如其他语言如C++或C#。 4. **套接字...

    jmx-clt:JMX 命令行工具提供轻量级 Unix 命令行访问以与远程 Java 进程通信

    JMX 命令行工具提供轻量级 Unix 命令行访问以与远程 Java 进程通信。 好处如下: 快速查看应用程序发生的情况 从文本终端操作 利用可插入的配置文件 配置文件允许您仅分析相关指标,并将其格式化以使其更具可读性。...

    Java基于socket的进程间通信 聊天小程序

    Java基于Socket的进程间通信(IPC)是一种网络编程技术,常用于实现客户端和服务器之间的通信。在这个场景下,我们讨论的是一个简单的聊天小程序,它利用了Java的Socket库来搭建客户端与服务器之间的桥梁,实现数据...

    Java模拟操作系统(内存管理,进程管理,文件管理,进程通信)

    Java模拟操作系统是一个基于Java编程语言实现的微型操作系统模型,它主要涵盖了四个核心领域:内存管理、进程管理、文件管理和进程通信。这样的项目旨在帮助开发者理解操作系统的工作原理,并提供了一个实践平台来...

Global site tag (gtag.js) - Google Analytics