进程间通信方式
管道(Pipe)
一、是什么?
管道是半双工的单向通信通道,数据只能单向流动(一个进程写,另一个进程读)。分为:
- 匿名管道:仅用于父子进程或有亲缘关系的进程
- 命名管道(FIFO):无亲缘关系的进程可通过文件系统路径访问
二、解决什么问题
解决有亲缘关系进程间的简单数据传递问题,如命令行中的 |
操作符(ls | grep txt
)。
三、应用场景
- Shell命令链中进程的数据传递
- 父子进程间的简单通信
- 日志收集系统(生产者-消费者模型)
四、Java示例
java
// 使用ProcessBuilder创建管道
ProcessBuilder processBuilder1 = new ProcessBuilder("echo", "Hello Pipe");
ProcessBuilder processBuilder2 = new ProcessBuilder("grep", "Hello");
// 连接两个进程
processBuilder1.redirectOutput(ProcessBuilder.Redirect.PIPE);
processBuilder2.redirectInput(processBuilder1.redirectOutput());
Process p1 = processBuilder1.start();
Process p2 = processBuilder2.start();
// 读取结果
BufferedReader reader = new BufferedReader(new InputStreamReader(p2.getInputStream()));
System.out.println(reader.readLine()); // 输出:Hello Pipe
五、重要注意事项
- 匿名管道只能用于亲缘进程
- 数据是字节流,无消息边界
- 缓冲区有限(通常4KB),写满时写操作阻塞
消息队列(Message Queue)
一、是什么?
内核维护的消息链表,进程通过唯一标识符访问队列,支持结构化消息传递。
二、解决什么问题
解决无亲缘关系进程间的异步通信需求,支持消息优先级和类型过滤。
三、应用场景
- 订单处理系统(生产者-消费者解耦)
- 微服务间通信
- 跨进程事件通知(如系统监控告警)
四、Java实现方式
Java标准库不直接支持系统级消息队列,但可通过中间件实现:
java
// 使用RabbitMQ示例(需引入amqp-client)
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.queueDeclare("IPC_QUEUE", false, false, false, null);
// 发送消息
channel.basicPublish("", "IPC_QUEUE", null, "Hello MQ".getBytes());
}
五、重要注意事项
- 消息需要序列化/反序列化
- 内核队列有最大长度限制
- 持久化消息影响性能
共享内存(Shared Memory)
一、是什么?
多个进程直接访问同一块物理内存区域,是速度最快的IPC方式。
二、解决什么问题
解决大数据量、高性能要求的进程通信场景(如视频处理)。
三、应用场景
- 数据库缓冲池管理
- 实时图像/视频处理
- 高频交易系统
四、Java实现
通过内存映射文件实现:
java
// 进程A:写入共享内存
RandomAccessFile file = new RandomAccessFile("shmem.dat", "rw");
FileChannel channel = file.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
buffer.put("Shared Data".getBytes());
// 进程B:读取相同文件
MappedByteBuffer buffer2 = new RandomAccessFile("shmem.dat", "rw")
.getChannel()
.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
byte[] data = new byte[11];
buffer2.get(data);
System.out.println(new String(data)); // 输出:Shared Data
五、重要注意事项
- 必须配合信号量等同步机制
- 内存一致性需开发者保证
- 复杂数据类型需要序列化
Socket
一、是什么?
基于网络协议的通信端点,支持TCP(可靠流)和UDP(数据报)两种模式。
二、解决什么问题
解决跨网络或同主机任意进程间的通用通信问题。
三、应用场景
- 分布式系统通信
- 微服务架构
- 浏览器-服务器交互
四、Java示例
java
// 服务端
try (ServerSocket server = new ServerSocket(8080);
Socket client = server.accept();
PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
out.println("Hello from Server!");
}
// 客户端
try (Socket socket = new Socket("localhost", 8080);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
System.out.println(in.readLine()); // 输出:Hello from Server!
}
五、重要注意事项
- TCP保证可靠但开销大,UDP高效但不保证可靠
- 需要处理网络异常
- JDK12+支持Reactive Socket(异步非阻塞)
信号(Signal)
一、是什么?
内核向进程发送的异步事件通知(如SIGINT对应Ctrl+C)。
二、解决什么问题
处理进程异常终止、用户中断等紧急事件。
三、应用场景
- 优雅关闭服务(SIGTERM)
- 程序调试(SIGTRAP)
- 子进程状态变更通知(SIGCHLD)
四、Java处理
java
// 注册关闭钩子(处理SIGTERM)
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Cleaning resources before exit");
}));
// 自定义信号处理(Unix-like系统)
sun.misc.Signal.handle(new sun.misc.Signal("USR1"), sig -> {
System.out.println("Received SIGUSR1");
});
五、重要注意事项
- 信号处理函数应保持简单
- 某些信号不可捕获(如SIGKILL)
- Windows支持有限
信号量(Semaphore)
一、是什么?
用于进程同步的计数器,控制对共享资源的访问权限。
二、解决什么问题
解决多进程竞争共享资源时的互斥与同步问题。
三、应用场景
- 数据库连接池管理
- 打印机等独占设备调度
- 生产者-消费者缓冲区控制
四、Java实现
Java信号量用于线程级同步,进程间需用JNI:
java
// 线程级信号量
Semaphore semaphore = new Semaphore(3); // 允许3个并发访问
void accessResource() {
semaphore.acquire();
try { /* 使用共享资源 */ }
finally { semaphore.release(); }
}
五、重要注意事项
- 需要防止死锁(获取顺序一致)
- 信号量不包含数据传递功能
- System V信号量支持跨进程
核心区别对比
方式 | 速度 | 亲缘要求 | 数据能力 | 同步需求 | 跨主机 |
---|---|---|---|---|---|
管道 | 中 | 匿名管道需要 | 字节流 | 内置同步 | ❌ |
消息队列 | 中 | ❌ | 结构化消息 | 可选 | ❌ |
共享内存 | 极快 | ❌ | 任意大数据 | 需要同步 | ❌ |
Socket | 慢 | ❌ | 字节流/数据报 | 内置 | ✅ |
信号 | 极快 | ❌ | 仅通知无数据 | 异步 | ❌ |
信号量 | 快 | ❌ | 无数据 | 同步控制 | ❌ |
总结
- 性能优先:选择共享内存(需配合信号量同步)
- 简单通信:亲缘进程用管道,非亲缘用消息队列
- 网络/分布式:必须使用Socket
- 紧急事件:信号处理关键中断
- 资源控制:信号量解决并发访问问题
JDK12+新特性:
java.net
包增强支持HTTP/2和WebSocket- 响应式编程模型(Reactive Streams)优化Socket通信
Foreign-Memory Access API
(JEP 370)改进共享内存操作
实际开发中,优先考虑:
- 单机高性能 → 共享内存+信号量
- 服务解耦 → 消息队列(如Kafka/RabbitMQ)
- 通用解决方案 → Socket(HTTP/RESTful API)