文档章节

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

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

一看到 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
浦东
高级程序员
私信 提问
Java NIO 机制分析(一) Java IO的演进

一、引言 Java1.4之前的早期版本,Java对I/O的支持并不完善,开发人员再开发高性能I/O程序的时候,会面临一些巨大的挑战和困难,主要有以下一些问题: (1)没有数据缓冲区,I/O性能存在问题...

宸明
04/20
0
0
(代码篇)从基础文件IO说起虚拟内存,内存文件映射,零拷贝

上一篇讲解了基础文件IO的理论发展,这里结合java看看各项理论的具体实现。 传统IO-intsmaze 传统文件IO操作的基础代码如下: FileInputStream in = new FileInputStream("D:\java.txt");in....

intsmaze(刘洋)
08/01
0
0
Java并发教程-1进程和线程

http://www.iteye.com/magazines/131 计算机的使用者一直以为他们的计算机可以同时做很多事情。他们认为当其他的应用程序在下载文件,管理打印队列或者缓冲音频的时候他们可以继续在文字处理...

noday
2014/04/25
0
0
JAVA NIO之浅谈内存映射文件原理与DirectMemory

JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段。本文我主要想结合操作系统中(OS)相关方面的知识...

pczhangtl
2013/11/19
0
0
深入了解 Java-Netty高性能高并发理解

一丶 Netty基础入门 Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,...

架构师springboot
10/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring异常之Druid – unregister mbean error

Spring异常之Druid – unregister mbean error 2017年04月19日 12:13:42 Dr.Zhu 阅读数:6688 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zt_fucker/arti...

linjin200
15分钟前
1
0
微信小程序webview问题

今天在改小程序的时候在使用webview的时候切换webview的地址行为,出现了诡异的情况。 默认querystring里会有多个?符号,使用的时候被微信给截取了,导致程序找不到改页面。 而且querystri...

钟元OSS
18分钟前
1
0
Spark2.0操作Hbase

读写Hbase的方法,这里是通过Spark的RDD来操作的方法,通过Hbase API的方式是另一种,这里不涉及。 首先配置pom,添加hbase依赖,一般Spark平台不包含hbase的jar包,所以这些依赖不添加<scop...

守望者之父
19分钟前
1
0
【转】你会用哪些JavaScript循环遍历

总结JavaScript中的循环遍历定义一个数组和对象 const arr = ['a', 'b', 'c', 'd', 'e', 'f'];const obj = {a: 1,b: 2,c: 3,d: 4} for() 经常用来遍历数组元素 遍历值为数组元素...

kaixin_code
21分钟前
1
0
mysql的锁

MySQL的锁 全局锁:对数据库实例加锁 MySQL提供了一个加全局读锁的方法:Flush tables with read lock(FTWRL) 使用场景:做全库逻辑备份。 官方自带的逻辑备份工具mysqldump,使用时带上参数...

灯下草虫鸣_
25分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部