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

本文介绍了Java中通过映像文件实现的共享内存来进行进程通信,这种方式相较于Socket通信能减少资源浪费,提高效率。文章详细阐述了共享内存的优缺点、特点、应用场景以及开发要点,并提供了具体的Java代码示例,展示了如何使用MappedByteBuffer和FileChannel进行读写操作。此外,还强调了在读写过程中使用文件锁确保数据安全的重要性。
摘要由CSDN通过智能技术生成

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.代码实测:

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

importcom.alibaba.fastjson.JSONObject;importjava.io.File;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.nio.ByteBuffer;importjava.nio.MappedByteBuffer;importjava.nio.channels.FileChannel;importjava.nio.channels.FileLock;public classShareMemory {int flen = 5242880; //开辟共享内存大小 50M

int fsize = 0; //文件的实际大小

String shareFileName; //共享内存文件名

String sharePath; //共享内存路径

MappedByteBuffer mapBuf = null; //定义共享内存缓冲区

FileChannel fc = null; //定义相应的文件通道

FileLock fl = null; //定义文件区域锁定的标记。

RandomAccessFile RAFile = null; //定义一个随机存取文件对象

/***

*@paramsp 共享内存文件路径

*@paramsf 共享内存文件名*/

publicShareMemory(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

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

}

}/***

*@paramps 锁定区域开始的位置;必须为非负数

*@paramlen 锁定区域的大小;必须为非负数

*@parambuff 写入的数据

*@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();returnlen;

}

}catch(Exception e) {if (fl != null) {try{

fl.release();

}catch(IOException e1) {

System.out.println(e1.toString());

}

}return 0;

}return 0;

}/***

*@paramps 锁定区域开始的位置;必须为非负数

*@paramlen 锁定区域的大小;必须为非负数

*@parambuff 要取的数据

*@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=mapBuf.remaining();

}if (len > 0) {

mapBuf.get(buff,0, len);

}

fl.release();returnlen;

}

}catch(Exception e) {if (fl != null) {try{

fl.release();

}catch(IOException e1) {

System.out.println(e1.toString());

}

}return 0;

}return 0;

}/*** 完成,关闭相关操作*/

protected void finalize() throwsThrowable {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 voidcloseSMFile() {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[]) throwsException{try{

ShareMemory sm= new ShareMemory("/home/lab/test","22222");

JSONObject json= newJSONObject();

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.附文件操作工具类

importjava.io.File;importjava.io.IOException;public classFileUtil {//验证字符串是否为正确路径名的正则表达式

private static String matches = "[A-Za-z]:\\\\[^:?\">

boolean flag = false;

File file;/*** 创建目录

*@paramdestDirName 需要创建目录的路径

*@return

*/

public static booleancreateDir(String destDirName) {

File dir= newFile(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;

}

}/*** 根据路径删除指定的目录或文件,无论存在与否

*@paramdeletePath 指定的文件的目录

*@return

*/

public booleanDeleteFolder(String deletePath) {

flag= false;if(deletePath.matches(matches)) {

file= newFile(deletePath);if (!file.exists()) {//判断目录或文件是否存在

return flag; //不存在返回 false

} else{if (file.isFile()) {//判断是否为文件

return deleteFile(deletePath);//为文件时调用删除文件方法

} else{return deleteDirectory(deletePath);//为目录时调用删除目录方法

}

}

}else{

System.out.println("要传入正确路径!");return false;

}

}/*** 删除单个文件

*@paramfilePath 文件路径

*@return

*/

public booleandeleteFile(String filePath) {

flag= false;

file= newFile(filePath);if (file.isFile() && file.exists()) {//路径为文件且不为空则进行删除

file.delete();//文件删除

flag = true;

}returnflag;

}/*** 删除目录(文件夹)以及目录下的文件

*@paramdirPath

*@return

*/

public booleandeleteDirectory(String dirPath) {//如果sPath不以文件分隔符结尾,自动添加文件分隔符

if (!dirPath.endsWith(File.separator)) {

dirPath= dirPath +File.separator;

}

File dirFile= newFile(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;

}

}/*** 创建单个文件

*@paramfilePath 文件路径

*@return

*/

public static booleancreateFile(String filePath) {

File file= newFile(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;

}

}/*** 创建临时文件

*@paramprefix

*@paramsuffix

*@paramdirName

*@return

*/

public staticString 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;

}

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值