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

本文介绍如何利用 Java NIO 的 MappedByteBuffer 实现跨 JVM 进程间的数据通信。通过将同一物理文件映射到不同 Java 进程的内存空间中,实现了类似 Windows 系统内存映射文件的功能,可用于进程间数据共享。

一看到 Java NIO 的内存映射文件(MappedByteBuffer),让我立即就联想到 Windows 系统的内存映射文件。Windows 系统的内存映射文件能用来在多个进程间共享数据,即进程间的共享内存,是通过把同一块内存区域映射到不同进程的地址空间中,从而达到共享内存。

Java NIO 的内存映射文件和 Windows 系统下的一样,都能把物理文件的内容映射到内存中,那么 MappedByteBuffer 是否能用来在不同 Java 进程(JVM) 间共享数据呢?答案是肯定的,这样在通常的 Socket 方式来实现 Java 进程间通信之上又多了一种方法。

在 Windows 中内存映射文件可以是脱离物理文件而存在的一块命名的内存区域,使用相同的内存映射名就能在不同的进程中共享同一片内存。然后,Java 的 MappedByteBuffer 总是与某个物理文件相关的,因为不管你是从 FileInputStream、FileOutputStream 还是 RandomAccessFile 得来的 FileChannel,再 map() 得到的内存映射文件 MappedByteBuffer,如果在构造 FileInputStream、FileOutputStream、RandomAccessFile 对象时不指定物理文件便会有 FileNotFoundException 异常。

所以 Java NIO 来实现共享内存的办法就是让不同进程的内存映射文件关联到同一个物理文件,因为 MappedByteBuffer 能让内存与文件即时的同步内容。严格说来,称之为内存共享是不准确的,其实就是两个 Java 进程通过中间文件来交换数据,用中间文件使得两个进程的两块内存区域的内容得到及时的同步。

用图来理解 Java NIO 的“共享内存”的实现原理:

知道了实现原理之后,下面用代码来演示两个进程间用内存映射文件来进行数据通信。代码 WriteShareMemory.java 往映射文件中依次写入 A、B、C ... Z,ReadShareMemory.java 逐个读出来,打印到屏幕上。代码对交换文件 swap.mm 的第一个字节作了读写标志,分别是 0-可读,1-正在写,2-可读。RandomAccessFile 得到的 Channel 能够灵活的进行读或写,并且不会破坏原有文件内容,而 FileInputStream 或 FileOutputStream 取得的 Channel 则很难达到这一功效,所以使用了 RandomAccessFile 来获得 FileChannel。

WriteShareMemory.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.unmi;
 
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
 
/**
  * 往 "共享内存" 写入数据
  * @author Unmi
  */
public class WriteShareMemory {
 
     /**
      * @param args
      * @throws Exception
      */
     public static void main(String[] args) throws Exception {
         RandomAccessFile raf = new RandomAccessFile( "c:/swap.mm" , "rw" );
         FileChannel fc = raf.getChannel();
         MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0 , 1024 );
 
         //清除文件内容
         for ( int i= 0 ;i< 1024 ;i++){
             mbb.put(i,( byte ) 0 );
         }
 
         //从文件的第二个字节开始,依次写入 A-Z 字母,第一个字节指明了当前操作的位置
         for ( int i= 65 ;i< 91 ;i++){
             int index = i- 63 ;
             int flag = mbb.get( 0 ); //可读标置第一个字节为 0
             if (flag != 0 ){ //不是可写标示 0,则重复循环,等待
                 i --;
                 continue ;
             }
             mbb.put( 0 ,( byte ) 1 ); //正在写数据,标志第一个字节为 1
             mbb.put( 1 ,( byte )(index)); //写数据的位置
 
             System.out.println( "程序 WriteShareMemory:" +System.currentTimeMillis() +
                     ":位置:" + index + " 写入数据:" + ( char )i);
 
             mbb.put(index,( byte )i); //index 位置写入数据
             mbb.put( 0 ,( byte ) 2 ); //置可读数据标志第一个字节为 2
             Thread.sleep( 513 );
         }
     }
}

ReadShareMemory.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.unmi;
 
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
 
/**
  * 从 "共享内存" 读出数据
  * @author Unmi
  */
public class ReadShareMemory {
 
     /**
      * @param args
      * @throws Exception
      */
     public static void main(String[] args) throws Exception {
         RandomAccessFile raf = new RandomAccessFile( "c:/swap.mm" , "rw" );
         FileChannel fc = raf.getChannel();
         MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0 , 1024 );
         int lastIndex = 0 ;
 
         for ( int i= 1 ;i< 27 ;i++){
             int flag = mbb.get( 0 ); //取读写数据的标志
             int index = mbb.get( 1 ); //读取数据的位置,2 为可读
 
             if (flag != 2 || index == lastIndex){ //假如不可读,或未写入新数据时重复循环
                 i--;
                 continue ;
             }
 
             lastIndex = index;
             System.out.println( "程序 ReadShareMemory:" + System.currentTimeMillis() +
                     ":位置:" + index + " 读出数据:" + ( char )mbb.get(index));
 
             mbb.put( 0 ,( byte ) 0 ); //置第一个字节为可读标志为 0
 
             if (index == 27 ){ //读完数据后退出
                 break ;
             }
         }
     }
}

在 Eclipse 中运行 WriteShareMemory,然后到命令行下运行 ReadShareMemory,你将会看到 WriteShareMemory 写一个字符,ReadShareMemory 读一个。

代码中使用了读写标志位,和写入的索引位置,所以在 WriteShareMemory 写入一个字符后,只有等待 ReadShareMemory 读出刚写入的字符后才会写入第二个字符。实际应用中可以加入更好的通知方式,如文件锁等。

你也可以查看执行时 c:\swap.mm 文件的内容来验证这一过程,因为 MappedByteBuffer 在运行时是一种 DirectByteBuffer,所以它能与文件即时的同步内容,无须通过 FileChannel 来 write(buffer) 往文件中手工写入数据,或 read(buffer) 手工读数据到内存中。

参考:1. 共享内存在Java中实现和应用

本文链接 http://unmi.cc/java-nio-memory-mapping-communicate , 来自 隔叶黄莺 Unmi Blog

【无人机】基于改进粒子群算法的无人机路径规划研究[和遗传算法、粒子群算法进行比较](Matlab代码实现)内容概要:本文围绕基于改进粒子群算法的无人机路径规划展开研究,重点探讨了在复杂环境中利用改进粒子群算法(PSO)实现无人机三维路径规划的方法,并将其遗传算法(GA)、标准粒子群算法等传统优化算法进行对比分析。研究内容涵盖路径规划的多目标优化、避障策略、航路点约束以及算法收敛性和寻优能力的评估,所有实验均通过Matlab代码实现,提供了完整的仿真验证流程。文章还提到了多种智能优化算法在无人机路径规划中的应用比较,突出了改进PSO在收敛速度和全局寻优方面的优势。; 适合人群:具备一定Matlab编程基础和优化算法知识的研究生、科研人员及从事无人机路径规划、智能优化算法研究的相关技术人员。; 使用场景及目标:①用于无人机在复杂地形或动态环境下的三维路径规划仿真研究;②比较不同智能优化算法(如PSO、GA、蚁群算法、RRT等)在路径规划中的性能差异;③为多目标优化问题提供算法选型和改进思路。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注算法的参数设置、适应度函数设计及路径约束处理方式,同时可参考文中提到的多种算法对比思路,拓展到其他智能优化算法的研究改进中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值