文档章节

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

涩女郎
 涩女郎
发布于 2015/04/23 11:07
字数 1286
阅读 130
收藏 14
点赞 0
评论 0

一看到 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);

        }

    }

}

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

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) 手工读数据到内存中。


© 著作权归作者所有

共有 人打赏支持
涩女郎
粉丝 35
博文 104
码字总数 160210
作品 0
浦东
高级程序员

暂无相关文章

20.zip压缩 tar打包 打包并压缩

6月25日任务 6.5 zip压缩工具 6.6 tar打包 6.7 打包并压缩 6.5 zip压缩工具: zip支持压缩目录 zip压缩完之后原来的文件不删除 不同的文件内容其实压缩的效果不一样 文件内有很多重复的用xz压...

王鑫linux ⋅ 14分钟前 ⋅ 0

double类型数据保留四位小数的另一种思路

来源:透析公式处理,有时候数据有很长的小数位,有的时候由在四位以内,如果用一般的处理方法,那么不足四位的小树会补充0到第四位,这样子有点画蛇添足的感觉,不太好看。所以要根据小数的...

young_chen ⋅ 21分钟前 ⋅ 0

Python 优化 回溯下降算法

使用sympy构造表达式,实现回溯下降算法 画出函数图像,先使用暴力搜索,找到最小值约为2.5左右 然后选定初始点,开始进行回溯搜索,下降方向为负梯度方向 下降的误差与步数大致呈现下面的状...

阿豪boy ⋅ 26分钟前 ⋅ 0

Django配置163邮箱出现 authentication failed(535)错误解决方法

最近用Django写某网站,当配置163邮箱设置完成后,出现535错误即:smtplib.SMTPAuthenticationError: (535, b'Error: authentication failed') Django初始配置邮箱设置 EMAIL_HOST = "smtp.1...

陈墨轩_CJX ⋅ 27分钟前 ⋅ 0

用接口模拟可伸缩枚举(34)

1、枚举的可伸缩性最后证明都不是什么好点子 扩展类型的元素是基本类型实例,基本类型的实例却不是扩展类型的元素,很混乱 目前还没有很好的方法来枚举基本类型的所有元素,及其扩展 可伸缩性...

职业搬砖20年 ⋅ 31分钟前 ⋅ 0

Ubuntu18.04 IDEA快捷键无法使用

IDEA默认的回退到上一视图的快捷键是Ctrl + Alt + Left,在ubuntu中这个快捷键被占用了,在16.04中可以在界面中取消这个快捷键,但是18.04就看不到了,可以使用以下命令解决 gsettings set ...

Iceberg_XTY ⋅ 35分钟前 ⋅ 0

如何解决s权限位引发postfix及crontab异常

一、问题现象 业务反馈某台应用服务器,普通用户使用mutt程序发送邮件时,提示“postdrop warning: mail_queue_enter: create file maildrop/713410.6065: Permission denied”,而且普通用法...

问题终结者 ⋅ 47分钟前 ⋅ 0

Unable to load database on disk

由于磁盘空间满了以后,导致zookeeper异常退出,清理磁盘空间后,zk启动报错,信息如下: 2018-06-25 17:18:46,904 INFO org.apache.zookeeper.server.quorum.QuorumPeerConfig: Reading co...

刀锋 ⋅ 今天 ⋅ 0

css3 box-sizing:border-box 实现div一行多列

<!DOCTYPE html><html><head><style> div.container{ background:green; padding:10px 10px;}div.box{box-sizing:border-box;-moz-box-sizing:border-box; /* Fir......

qimh ⋅ 今天 ⋅ 0

Homebrew简介和基本使用

一、Homebrew是什么 Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径...

说回答 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部