Java开发网 Java开发网
注册 | 登录 | 帮助 | 搜索 | 排行榜 | 发帖统计  

您没有登录

» Java开发网 » 技术文章库  

按打印兼容模式打印这个话题 打印话题    把这个话题寄给朋友 寄给朋友    该主题的所有更新都将Email到你的邮箱 订阅主题
flat modethreaded modego to previous topicgo to next topicgo to back
作者 共享内存在Java中的实现和应用
zls





发贴: 75
积分: 30
于 2003-08-17 02:12 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
对熟知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应用中还是很有用的,只要组织好共享内存的数据结构,共享内存就可以在应用开发中发挥很不错的作用。

> 摘自IBM developerworks.

共享内存在Java中的实现和应用.mht (42.95k)



作者 Re:共享内存在Java中的实现和应用 [Re:zls]
空灵



发贴: 0
积分: 0
于 2003-09-12 23:08 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
楼上的兄弟请问:
如果我要释放独占锁该怎么做呢
释放文件通道该怎么做
mapbuf.flip()在这里什么意思



作者 Re:共享内存在Java中的实现和应用 [Re:zls]
空灵



发贴: 0
积分: 0
于 2003-09-12 23:14 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
忘了问个问题:
MappedByteBuffer这个类是在哪个包里的(具体点)



作者 Re:共享内存在Java中的实现和应用 [Re:zls]
liyangtom



发贴: 0
积分: 0
于 2003-09-22 15:20 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
这是我写的共享内存代码,用于守护进程交互数据的,供大家参考!

import java.io.*;
import java.util.*;
import java.nio.channels.*;

/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright Coffee 2003</p>
* <p>Company: Zhejiang University</p>
* @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";
}



作者 Re:共享内存在Java中的实现和应用 [Re:zls]
zls





发贴: 75
积分: 30
于 2003-09-27 23:46 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
楼上的“空灵”,我对共享内存研究不多,对于你的问题能否请你自己研究研究jdk文挡。



flat modethreaded modego to previous topicgo to next topicgo to back
  已读帖子
  新的帖子
  被删除的帖子
Jump to the top of page

   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923