博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java进程通信之映像文件共享内存
阅读量:6849 次
发布时间:2019-06-26

本文共 13094 字,大约阅读时间需要 43 分钟。

Java进程通信之映像文件共享内存

  1. 共享内存 vs 进程通信

  对UNIX系统来说,共享内存分为一般共享内存和映像文件共享内存两种.但windows实际上只有影像文件共享内存一种.

  而说到进程通信,First当然是Socket通信,但缺点太明显.其一,浪费网络资源,其二,多余的code成本也绝非所愿.

  综上,映像文件共享内存方式,成为了实际应用中推荐使用的进程通信手段. 

  2.优点

  数据共享/动态配置/减少资源浪费

  3.特点

  • 可被多个进程打开访问
  • 读写操作的进程在执行读写操作时,其他进程不能进行写操作
  • 多个进程可以交替对某一共享内存执行写操作
  • 一个进程执行了内存的写操作后,不影响其他进程对该内存的访问。同时其他进程对更新后的内存具有可见性
  • 在进程执行写操作时,如果异常退出,对其他进程写操作禁止应自动解除

  4.常见场景

  1、独占的写操作,相应有独占的写操作等待队列。独占的写操作本身不会发生数据的一致性问题。

  2、共享的写操作,相应有共享的写操作等待队列。共享的写操作则要注意防止发生数据的一致性问题。  

  3、独占的读操作,相应有共享的读操作等待队列。

  4、共享的读操作,相应有共享的读操作等待队列。

  5.开发要点

  JDK 1.4新增的 MappedByteBuffer类 提供了实现共享内存的方法,该缓冲区实际上是一个磁盘文件的内存影像.二者的变化保持同步,即内存数据发生变化会立刻反映到磁盘文件中,进而有效的保证共享内存的实现.

  FileChannel 是将共享内存和磁盘文件建立联系的文件通道类。FileChannel 类的加入是 JDK 为了统一对外设备(文件、网络接口等)的访问方法,并加强了多线程对同一文件进行存取的安全性。在这里用它来建立共享内存和磁盘文件间的一个通道。  

  RandomAccessFile 是 Java IO 体系中功能最丰富的文件内容访问类,它提供很多方法来操作文件,包括读写支持,与普通的IO流相比,它最大的特别之处就是支持任意访问的方式,程序可以直接跳到任意地方来读写数据。举例:向一个5G的文件中新增一行文字,"update by nya".可以直接使用 Java 中的流读取 txt 文本里所有的数据转成字符串后,随后拼接文字,再写回文本即可.而如果内存不充裕,则可以使用RandomAccessFile类来完成,可以实现零内存追加,这就是其支持任意位置读写类的强大之处.

  6.代码实测:

  此处读写进程,采用文件锁来保证数据读写安全.实操可用

import com.alibaba.fastjson.JSONObject;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import java.nio.ByteBuffer;import java.nio.MappedByteBuffer;import java.nio.channels.FileChannel;import java.nio.channels.FileLock;public class ShareMemory {    int flen = 5242880;                    //开辟共享内存大小 50M    int fsize = 0;                          //文件的实际大小    String shareFileName;                   //共享内存文件名    String sharePath;                       //共享内存路径    MappedByteBuffer mapBuf = null;         //定义共享内存缓冲区    FileChannel fc = null;                  //定义相应的文件通道    FileLock fl = null;                     //定义文件区域锁定的标记。    RandomAccessFile RAFile = null;         //定义一个随机存取文件对象    /**     *     * @param sp    共享内存文件路径     * @param sf    共享内存文件名     */    public ShareMemory(String sp, String sf) {        if (sp.length() != 0) {            FileUtil.createDir(sp);            this.sharePath = sp + File.separator;        } else {            this.sharePath = sp;        }        this.shareFileName = sf;        try {            // 获得一个只读的随机存取文件对象   "rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。            RAFile = new RandomAccessFile(this.sharePath + this.shareFileName + ".sm", "rw");            //获取相应的文件通道            fc = RAFile.getChannel();            //获取实际文件的大小            fsize = (int) fc.size();            if (fsize < flen) {                byte bb[] = new byte[flen - fsize];                //创建字节缓冲区                ByteBuffer bf = ByteBuffer.wrap(bb);                bf.clear();                //设置此通道的文件位置。                fc.position(fsize);                //将字节序列从给定的缓冲区写入此通道。                fc.write(bf);                fc.force(false);                fsize = flen;            }            //将此通道的文件区域直接映射到内存中。            mapBuf = fc.map(FileChannel.MapMode.READ_WRITE, 0, fsize);        } catch (IOException e) {            e.printStackTrace();        }    }    /**     *     * @param ps        锁定区域开始的位置;必须为非负数     * @param len       锁定区域的大小;必须为非负数     * @param buff      写入的数据     * @return     */    public synchronized int write(int ps, int len, byte[] buff) {        if (ps >= fsize || ps + len >= fsize) {            return 0;        }        try {            //获取此通道的文件给定区域上的锁定。            fl = fc.lock(ps, len, false);            if (fl != null) {                // 清除文件内容                // 清除文件内容,对MappedByteBuffer的操作就是对文件的操作                for (int i = ps ; i < (ps + len); i++) {                    mapBuf.put(i,(byte)0);                }                mapBuf.position(ps);                ByteBuffer bf1 = ByteBuffer.wrap(buff);                mapBuf.put(bf1);                //释放此锁定。                fl.release();                return len;            }        } catch (Exception e) {            if (fl != null) {                try {                    fl.release();                } catch (IOException e1) {                    System.out.println(e1.toString());                }            }            return 0;        }        return 0;    }    /**     *     * @param ps        锁定区域开始的位置;必须为非负数     * @param len       锁定区域的大小;必须为非负数     * @param buff      要取的数据     * @return     */    public synchronized int read(int ps, int len, byte[] buff) {        if (ps >= fsize) {            return 0;        }        //定义文件区域锁定的标记。        try {            fl = fc.lock(ps, len, false);            if (fl != null) {                //System.out.println( "ps="+ps );                mapBuf.position(ps);                if (mapBuf.remaining() < len) {                    len = mapBuf.remaining();                }                if (len > 0) {                    mapBuf.get(buff, 0, len);                }                fl.release();                return len;            }        } catch (Exception e) {            if (fl != null) {                try {                    fl.release();                } catch (IOException e1) {                    System.out.println(e1.toString());                }            }            return 0;        }        return 0;    }    /**     * 完成,关闭相关操作     */    protected void finalize() throws Throwable {        if (fc != null) {            try {                fc.close();            } catch (IOException e) {                System.out.println(e.toString());            }            fc = null;        }        if (RAFile != null) {            try {                RAFile.close();            } catch (IOException e) {                System.out.println(e.toString());            }            RAFile = null;        }        mapBuf = null;    }    /**     * 关闭共享内存操作     */    public synchronized void closeSMFile() {        if (fc != null) {            try {                fc.close();            } catch (IOException e) {                System.out.println(e.toString());            }            fc = null;        }        if (RAFile != null) {            try {                RAFile.close();            } catch (IOException e) {                System.out.println(e.toString());            }            RAFile = null;        }        mapBuf = null;    }    public static void main(String arsg[]) throws Exception{        try {            ShareMemory sm = new ShareMemory("/home/lab/test","22222");            JSONObject json = new JSONObject();            json.put("name","来兮子宁");            json.put("age",18);            String str = json.toJSONString();//            StringBuffer sb = new StringBuffer();//            for (int i = 0 ; i < 100000; i++) {//                sb.append("小米刚同学").append("\t");//            }//            System.out.println(sb.toString());            sm.write(40, 50000000, str.getBytes("UTF-8"));//            sm.write(40, 50000000, sb.toString().getBytes("UTF-8"));            byte[] b = new byte[50000000];            sm.read(40, 50000000, b);            String jsonStr = new String(b, "UTF-8").trim();            System.out.println(jsonStr);            JSONObject jsonObject = (JSONObject) JSONObject.parse(jsonStr);            String name = jsonObject.getString("name");            Integer age = jsonObject.getInteger("age");            System.out.println("name : " + name + " age : " + age);        } catch (Exception e) {            e.printStackTrace();        }    }}

  6.参考资料

  Java进程通信(共享内存) - https://chenhy.com/post/java_ipc/

  Java实现共享内存操作 - https://huxu1986-163-com.iteye.com/blog/2163251

  7.附文件操作工具类

import java.io.File;import java.io.IOException;public class FileUtil {    // 验证字符串是否为正确路径名的正则表达式    private static String matches = "[A-Za-z]:\\\\[^:?\"><*]*";    // 通过 sPath.matches(matches) 方法的返回值判断是否正确    // sPath 为路径字符串    boolean flag = false;    File file;    /**     *  创建目录     * @param destDirName 需要创建目录的路径     * @return     */    public static boolean createDir(String destDirName) {        File dir = new File(destDirName);        if (dir.exists()) {
// 判断目录是否存在 return false; } if (!destDirName.endsWith(File.separator)) {
// 结尾是否以"/"结束 destDirName = destDirName + File.separator; } if (dir.mkdirs()) {
// 创建目标目录 System.out.println("创建目录成功!" + destDirName); return true; } else { System.out.println("创建目录失败!"); return false; } } /** * 根据路径删除指定的目录或文件,无论存在与否 * @param deletePath 指定的文件的目录 * @return */ public boolean DeleteFolder(String deletePath) { flag = false; if (deletePath.matches(matches)) { file = new File(deletePath); if (!file.exists()) {
// 判断目录或文件是否存在 return flag; // 不存在返回 false } else { if (file.isFile()) {
// 判断是否为文件 return deleteFile(deletePath);// 为文件时调用删除文件方法 } else { return deleteDirectory(deletePath);// 为目录时调用删除目录方法 } } } else { System.out.println("要传入正确路径!"); return false; } } /** * 删除单个文件 * @param filePath 文件路径 * @return */ public boolean deleteFile(String filePath) { flag = false; file = new File(filePath); if (file.isFile() && file.exists()) {
// 路径为文件且不为空则进行删除 file.delete();// 文件删除 flag = true; } return flag; } /** * 删除目录(文件夹)以及目录下的文件 * @param dirPath * @return */ public boolean deleteDirectory(String dirPath) { // 如果sPath不以文件分隔符结尾,自动添加文件分隔符 if (!dirPath.endsWith(File.separator)) { dirPath = dirPath + File.separator; } File dirFile = new File(dirPath); // 如果dir对应的文件不存在,或者不是一个目录,则退出 if (!dirFile.exists() || !dirFile.isDirectory()) { return false; } flag = true; File[] files = dirFile.listFiles();// 获得传入路径下的所有文件 for (int i = 0; i < files.length; i++) {
// 循环遍历删除文件夹下的所有文件(包括子目录) if (files[i].isFile()) {
// 删除子文件 flag = deleteFile(files[i].getAbsolutePath()); System.out.println(files[i].getAbsolutePath() + " 删除成功"); if (!flag) break;// 如果删除失败,则跳出 } else {
// 运用递归,删除子目录 flag = deleteDirectory(files[i].getAbsolutePath()); if (!flag) break;// 如果删除失败,则跳出 } } if (!flag) return false; if (dirFile.delete()) {
// 删除当前目录 return true; } else { return false; } } /** * 创建单个文件 * @param filePath 文件路径 * @return */ public static boolean createFile(String filePath) { File file = new File(filePath); if (file.exists()) {
// 判断文件是否存在 System.out.println("目标文件已存在" + filePath); return false; } if (filePath.endsWith(File.separator)) {
// 判断文件是否为目录 System.out.println("目标文件不能为目录!"); return false; } if (!file.getParentFile().exists()) {
// 判断目标文件所在的目录是否存在 // 如果目标文件所在的文件夹不存在,则创建父文件夹 System.out.println("目标文件所在目录不存在,准备创建它!"); if (!file.getParentFile().mkdirs()) {
// 判断创建目录是否成功 System.out.println("创建目标文件所在的目录失败!"); return false; } } try { if (file.createNewFile()) {
// 创建目标文件 System.out.println("创建文件成功:" + filePath); return true; } else { System.out.println("创建文件失败!"); return false; } } catch (IOException e) {
// 捕获异常 e.printStackTrace(); System.out.println("创建文件失败!" + e.getMessage()); return false; } } /** * 创建临时文件 * @param prefix * @param suffix * @param dirName * @return */ public static String createTempFile(String prefix, String suffix, String dirName) { File tempFile = null; if (dirName == null) {
// 目录如果为空 try { tempFile = File.createTempFile(prefix, suffix);// 在默认文件夹下创建临时文件 return tempFile.getCanonicalPath();// 返回临时文件的路径 } catch (IOException e) {
// 捕获异常 e.printStackTrace(); System.out.println("创建临时文件失败:" + e.getMessage()); return null; } } else { // 指定目录存在 File dir = new File(dirName);// 创建目录 if (!dir.exists()) { // 如果目录不存在则创建目录 if (FileUtil.createDir(dirName)) { System.out.println("创建临时文件失败,不能创建临时文件所在的目录!"); return null; } } try { tempFile = File.createTempFile(prefix, suffix, dir);// 在指定目录下创建临时文件 return tempFile.getCanonicalPath();// 返回临时文件的路径 } catch (IOException e) {
// 捕获异常 e.printStackTrace(); System.out.println("创建临时文件失败!" + e.getMessage()); return null; } } }}

 

  

转载于:https://www.cnblogs.com/nyatom/p/10770182.html

你可能感兴趣的文章
tomcat 连接数设置(转)
查看>>
linux下定时执行任务的方法
查看>>
Activity 之生命周期
查看>>
Windows压缩包安装MySQL
查看>>
datatable编辑一行数据的方法
查看>>
转眼已是一年
查看>>
iOS - MVC 架构模式
查看>>
给类,实例绑定属性和方法
查看>>
windows内核对象
查看>>
将div一直保持到页面底部
查看>>
用Vue来实现音乐播放器(十八):右侧快速入口点击高亮
查看>>
js中test,exec和match方法
查看>>
JSP内置对象
查看>>
POJ - 3267 The Cow Lexicon(动态规划)
查看>>
javascript 弹出的窗口返回值给 父窗口
查看>>
封装ajax支持get、post
查看>>
动态引入js
查看>>
串口通信
查看>>
java 多线程(生产者消费者)
查看>>
php二维数组的排序
查看>>