免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1454 | 回复: 0
打印 上一主题 下一主题

共享内存在Java中的实现和应用 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-07-16 22:43 |只看该作者 |倒序浏览
      共享内存在Java中的实现和应用
      

    对熟知UNIX系统应用开发的程序员来说,IPC(InterProcess Communication)机制是 非常熟悉的,IPC基本包括共享内存、信号灯操作、消息队列、信号处理等部分,是开发应 用中非常重要的必不可少的工具。其中共享内存IPC机制的关键,对于数据共享、系统快 速查询、动态配置、减少资源耗费等均有独到的优点。
对应UNIX系统来说,共享内存分为一般共享内存和映像文件共享内存两种,而对应 Windows,实际上只有映像文件共享内存一种。所以java应用中也是只能创建映像文件共享 内存。
在java语言中,基本上没有提及共享内存这个概念,但是,在某一些应用中,共享内 存确实非常有用,例如采用java语言的分布式应用系统中,存在着大量的分布式共享对象, 很多时候需要查询这些对象的状态,以查看系统是否运行正常或者了解这些对象的目前的一 些统计数据和状态。如果采用网络通信的方式,显然会增加应用的额外负担,也增加了一些 不必要的应用编程。而如果采用共享内存的方式,则可以直接通过共享内存查看对象的状态 数据和统计数据,从而减少了一些不必要的麻烦。
共享内存的使用有如下几个特点:
可以被多个进程打开访问;
读写操作的进程在执行读写操作时其他进程不能进行写操作;
多个进程可以交替对某一共享内存执行写操作;
一个进程执行了内存的写操作后,不影响其他进程对该内存的访问。同时其他进程对更新后的内存具有可见性。
在进程执行写操作时如果异常退出,对其他进程写操作禁止应自动解除。
相对共享文件,数据访问的方便性和效率有
另外,共享内存的使用上有如下情况:
独占的写操作,相应有独占的写操作等待队列。独占的写操作本身不会发生数据的一致性问题。
共享的写操作,相应有共享的写操作等待队列。共享的写操作则要注意防止发生数据的一致性问题。
独占的读操作,相应有共享的读操作等待队列;
共享的读操作,相应有共享的读操作等待队列。
一般情况下,我们只是关心第一二种情况。
2 共享内存在java中的实现
在jdk1.4中提供的类MappedByteBuffer为我们实现共享内存提供了较好的方法。该缓 冲区实际上是一个磁盘文件的内存映像。二者的变化将保持同步,即内存数据发生变化会立 刻反映到磁盘文件中,这样会有效的保证共享内存的实现。
将共享内存和磁盘文件建立联系的是文件通道类:FileChannel。该类的加入是JDK为 了统一对外部设备(文件、网络接口等)的访问方法,并且加强了多线程对同一文件进行存 取的安全性。例如读写操作统一成read和write。这里只是用它来建立共享内存用,它建立 了共享内存和磁盘文件之间的一个通道。
打开一个文件建立一个文件通道可以用RandomAccessFile类中的方法getChannel。该 方法将直接返回一个文件通道。该文件通道由于对应的文件设为随机存取文件,一方面可以 进行读写两种操作,另一方面使用它不会破坏映像文件的内容(如果用FileOutputStream直 接打开一个映像文件会将该文件的大小置为0,当然数据会全部丢失)。这里,如果用 FileOutputStream和FileInputStream则不能理想的实现共享内存的要求,因为这两个类同时 实现自由的读写操作要困难得多。
下面的代码实现了如上功能,它的作用类似UNIX系统中的mmap函数。
// 获得一个只读的随机存取文件对象
RandomAccessFile RAFile = new RandomAccessFile(filename,"r");
// 获得相应的文件通道
FileChannel fc = RAFile.getChannel();
// 取得文件的实际大小,以便映像到共享内存
int size = (int)fc.size();
// 获得共享内存缓冲区,该共享内存只读
MappedByteBuffer mapBuf = fc.map(FileChannel.MAP_RO,0,size);
// 获得一个可读写的随机存取文件对象
RAFile = new RandomAccessFile(filename,"rw");
// 获得相应的文件通道
fc = RAFile.getChannel();
// 取得文件的实际大小,以便映像到共享内存
size = (int)fc.size();
// 获得共享内存缓冲区,该共享内存可读写
mapBuf = fc.map(FileChannel.MAP_RW,0,size);
// 获取头部消息:存取权限
mode = mapBuf.getInt();
如果多个应用映像同一文件名的共享内存,则意味着这多个应用共享了同一内存数据。 这些应用对于文件可以具有同等存取权限,一个应用对数据的刷新会更新到多个应用中。
为了防止多个应用同时对共享内存进行写操作,可以在该共享内存的头部信息加入写操 作标志。该共享内存的头部基本信息至少有:
int Length; // 共享内存的长度。
int mode; // 该共享内存目前的存取模式。
共享内存的头部信息是类的私有信息,在多个应用可以对同一共享内存执行写操作时, 开始执行写操作和结束写操作时,需调用如下方法:
public boolean StartWrite()
{
if(mode == 0) { // 标志为0,则表示可写
mode = 1; // 置标志为1,意味着别的应用不可写该共享内存
mapBuf.flip();
mapBuf.putInt(mode); // 写如共享内存的头部信息
return true;
}
else {
return false; // 指明已经有应用在写该共享内存,本应用不可写该共享内存
}
}
public boolean StopWrite()
{
mode = 0; // 释放写权限
mapBuf.flip();
mapBuf.putInt(mode); // 写入共享内存头部信息
return true;
}
这里提供的类文件mmap.java封装了共享内存的基本接口,读者可以用该类扩展成自己 需要的功能全面的类。
如果执行写操作的应用异常中止,那么映像文件的共享内存将不再能执行写操作。为了 在应用异常中止后,写操作禁止标志自动消除,必须让运行的应用获知退出的应用。在多线 程应用中,可以用同步方法获得这样的效果,但是在多进程中,同步是不起作用的。方法可 以采用的多种技巧,这里只是描述一可能的实现:采用文件锁的方式。写共享内存应用在获 得对一个共享内存写权限的时候,除了判断头部信息的写权限标志外,还要判断一个临时的 锁文件是否可以得到,如果可以得到,则即使头部信息的写权限标志为1(上述),也可以 启动写权限,其实这已经表明写权限获得的应用已经异常退出,这段代码如下:
// 打开一个临时的文件,注意同一共享内存,该文件名要相同,可以在共享文件名后加后缀“.lock”。
RandomAccessFile fis = new RandomAccessFile("shm.lock","rw");
// 获得文件通道
FileChannel lockfc = fis.getChannel();
// 获得文件的独占锁,该方法不产生堵塞,立刻返回
FileLock flock = lockfc.tryLock();
// 如果为空,则表明已经有应用占有该锁
if(flock == null) {
...// 不能执行写操作
}
else {
...// 可以执行写操作
}
该锁会在应用异常退出后自动释放,这正是该处所需要的方法。
3 共享内存在java中的应用
共享内存在java应用中,经常有如下两种种应用:
永久对象配置。
在java服务器应用中,用户可能会在运行过程中配置一些参数,而这些参数需要永久 有效,当服务器应用重新启动后,这些配置参数仍然可以对应用起作用。这就可以用到该文 中的共享内存。该共享内存中保存了服务器的运行参数和一些对象运行特性。可以在应用启 动时读入以启用以前配置的参数。
查询共享数据。
一个应用(例 sys.java)是系统的服务进程,其系统的运行状态记录在共享内存中,其 中运行状态可能是不断变化的。为了随时了解系统的运行状态,启动另一个应用(例 mon.java),该应用查询该共享内存,汇报系统的运行状态。
可见,共享内存在java应用中还是很有用的,只要组织好共享内存的数据结构,共享内存就可以在应用开发中发挥很不错的作用。

这是我写的共享内存代码,用于守护进程交互数据的,供大家参考!
import java.io.*;
import java.util.*;
import java.nio.channels.*;
/**
* Title:
* Description:
* Copyright: Copyright

2003
* Company: Zhejiang University
* @author Zhou Sen(周森)
* @version 1.0
*/
public class SharedFile
{
public SharedFile()
{
}
/** function: if the id was existed, modify the value.
* Otherwise, we put a new attribute to the shared file
* @param id -- the identify
* @param value -- the new value identified by id
* @return the old value identified by id
*/
public String setAttribute(String id, String value)
{
FileLock flock = null;
String oldValue = null;
FileChannel lockfc = null;
RandomAccessFile fis = null;
try
{
// 打开一个临时的文件,注意同一共享内存,该文件名要相同。
fis = new RandomAccessFile(filename,"rw");
// 获得文件通道
lockfc = fis.getChannel();
// 获得文件的独占锁,若文件已被人锁定,则一直等待
flock = lockfc.lock();
// read data from the shared file to a map
readDataFromFile(lockfc);
// put the entry to the map
oldValue = (String)recTreeMap.put(id,value);
// set the file point to the beginning of the file
lockfc.position(0);
// write data to the file from the treemap
writeDataToFile(lockfc);
}
catch(IOException ee)
{
ee.printStackTrace();
}
finally
{
// if flock is not null, we should release it
try
{
if( flock != null )
flock.release();
if( lockfc != null )
lockfc.close();
if( fis != null )
fis.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
return oldValue;
}
/** function:remove the attribute identifed by id from the shared file ,
* if the id is not existed, return null.
* @param id -- the identify
* @return oldvalue -- the value identified by id
*/
public String removeAttribute(String id)
{
FileLock flock = null;
String oldValue = null;
FileChannel lockfc = null;
RandomAccessFile fis = null;
try
{
// 打开一个临时的文件,注意同一共享内存,该文件名要相同。
fis = new RandomAccessFile(filename,"rw");
// 获得文件通道
lockfc = fis.getChannel();
// 获得文件的独占锁,若文件已被人锁定,则一直等待
flock = lockfc.lock();
// read data from the shared file to a map
readDataFromFile(lockfc);
// remove the entry to the map
oldValue = (String)recTreeMap.remove(id);
// set the file point to the beginning of the file
lockfc.position(0);
// write data to the file from the treemap
writeDataToFile(lockfc);
}
catch(IOException ee)
{
ee.printStackTrace();
}
finally
{
// if flock is not null, we should release it
try
{
if( flock != null )
flock.release();
if( lockfc != null )
lockfc.close();
if( fis != null )
fis.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
return oldValue;
}
/** function: get the value of an attribute identified by id from the shared file
* @param the identify
* @return the value identified by id
*/
public String getValue(String id)
{
FileLock flock = null;
String value = null;
RandomAccessFile fis = null;
FileChannel lockfc = null;
try
{
// 打开一个临时的文件,注意同一共享内存,该文件名要相同。
fis = new RandomAccessFile(filename,"rw");
// 获得文件通道
lockfc = fis.getChannel();
// 获得文件的独占锁,若文件已被人锁定,则一直等待
flock = lockfc.lock();
// read data from the shared file to a map
readDataFromFile(lockfc);
// get the value from the map identified by id
value = (String)recTreeMap.get(id);
}
catch(IOException ee)
{
ee.printStackTrace();
}
finally
{
// if flock is not null, we should release it
if( flock != null)
{
try
{
if( flock != null )
flock.release();
if( lockfc != null )
lockfc.close();
if( fis != null )
fis.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
return value;
}
// empty the file, deleting all record
public void emptyFile()
{
try
{
TreeMap tmpTreeMap = new TreeMap();
ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(filename) );
out.writeObject(tmpTreeMap); // write the recTreeMap to the file
      out.close();
}
catch(IOException ee)
{
ee.printStackTrace();
}
}
/** Read the data from the file to the map */
private void readDataFromFile(FileChannel channel) throws IOException
{
try
{
// create new record TreeMap
ObjectInputStream in = new ObjectInputStream(Channels.newInputStream(channel));
recTreeMap = (TreeMap)in.readObject(); // read the recTreeMap from the file
}
catch(IOException exp)
{
throw exp;
}
catch(ClassNotFoundException exp)
{
exp.printStackTrace();
}
}
/** Write the data from the map to the file */
private void writeDataToFile(FileChannel channel) throws IOException
{
ObjectOutputStream out;
try
{
out = new ObjectOutputStream(Channels.newOutputStream(channel));
out.writeObject(recTreeMap); // write the recTreeMap to the file
out.close();
}
catch(IOException exp)
{
throw exp;
}
  }
public void setFileName(String filename) {
this.filename = filename ;
this.emptyFile();
}
/** We Store all records in recTreeMap whose key is ID
* The struct of a entry in the recTreeMap
* ID Value
*/
private TreeMap recTreeMap = new TreeMap();
/** The filename corresponding to the TreeMap */
private String filename = "./sharefile";
}


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/99392/showart_1998679.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP