文档章节

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

牧师-Panda
 牧师-Panda
发布于 2016/10/20 14:00
字数 1089
阅读 24
收藏 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流对象的时候,必须要指定文件源。

所以 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。

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);
        }
    }
}
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;
            }
        }
    }
}

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

 

 

 

 

本文转载自:http://unmi.cc/java-nio-memory-mapping-communicate/

牧师-Panda
粉丝 32
博文 146
码字总数 180044
作品 0
浦东
私信 提问

暂无文章

0.01-Win10安装linux子系统

一、安装Debian子系统 -1、控制面板设置: -1.1、打开“控制面板” —— “程序” —— “启用或关闭Windows功能” —— 勾选 “适用于Linux的Windows子系统” -2、设置: -2.1、打开“设置”...

静以修身2025
昨天
1
0
init 0-6 (启动级别:init 0,1,2,3,4,5,6)

启动级别: init 0,1,2,3,4,5,6 这是个很久的知识点了,只是自己一直都迷迷糊糊的,今天在翻出来好好理解下。。 0: 停机 1:单用户形式,只root进行维护 2:多用户,不能使用net file system...

圣洁之子
昨天
2
0
Android Camera HAL浅析

1、Camera成像原理介绍 Camera工作流程图 Camera的成像原理可以简单概括如下: 景物(SCENE)通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为电信号,经过A/D(模数转...

天王盖地虎626
昨天
2
0
聊聊Elasticsearch的ProcessProbe

序 本文主要研究一下Elasticsearch的ProcessProbe ProcessProbe elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/monitor/process/ProcessProbe.java public class ProcessProb......

go4it
昨天
2
0
mysql PL(procedure language)流程控制语句

在MySQL中,常见的过程式SQL语句可以用在存储体中。其中包括IF语句、CASE语句、LOOP语句、WHILE语句、ITERATE语句和LEAVE语句,它们可以进行流程控制。 IF语句相当于Java中的if()...else if(...

edison_kwok
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部