网站首页
JSP空间
动态资讯
开源项目
技术文档
资源下载
J2EE资源
客户论坛
在线支付
 
  技术文档>>JAVA>>新手入门>>基础入门>查看文档  
  讨论如何避免java程序中的数据脏读问题     
  文章作者:未知  文章来源:赛迪网技术社区  
  查看:46次  录入:管理员--2007-11-16  
 

脏数据(out-of-date data),指过时的数据。
如果在您的java程序中存在脏数据,将或多或少地给软件系统带来一些问题,如:无法实时地应用已经发生改变的配置,软件系统出现一些莫名其妙的、难以重现的、后果严重的错误等等。尽量避免脏数据的存在是非常有价值的。本文希望能在这方面给同行们一点帮助。

fragment 1. 缓存技术的脏数据问题
/**
* a report printer is used to print a report.
*
* @version 1.0 9/9/2003
* @author bill
*/
public class reportprinter {
/**
* constructs a reportprinter instance.
*/
public reportprinter() {
// do something...
}

/**
* prints a printable.
*
* @param printable the specified printable object
*/
public void print(printable printable) {
graphics g = getgraphics();
g.setfont(getreportfont(printable.getfont());

printable.print(g);
}

/**
* returns the corresponding report font of a java font.
*
* @param javafont the specified java font
* @return the corresponding report font
*/
private font getreportfont(font javafont) {
font reportfont = fontmap.get(javafont);

if(reportfont == null) {
reportfont = loadfont(javafont);
fontmap.put(javafont, reportfont);
}

return reportfont;
}

/**
* loads the corresponding report font of a java font.
*
* @param javafont the specified java font
* @param the corresponding report font
*/
protected static font loadfont(font javafont) {
font reportfont = null;

// do something...

return reportfont;
}

/**
* the font map(java font->report font).
*/
private static hashmap fontmap = new hashmap();
}

fragment 1中,由于装载一个java font所对应的report font开销较大,使用了缓存技术来避免这种开销。这是一种常见的提高性能的方式,而且在一般情况下运行良好。但是fragment 1的设计与实现可能是不完备的,因为极有可能一个java font所对应的report font在系统启动之后发生变化,在这种变化发生之后,只有重启软件系统才能装载之,这常常是最终用户的抱怨之一。更可怕的是,类似的这种脏数据的存在还可能带来其它严重的、无法想象的后果。
如何避免使用缓存技术所带来的脏数据问题呢?
在设计、实现和测试时,应该清晰定义缓存数据的更新:
i. 不考虑缓存数据的更新,重启软件系统是一种必要的方式;
ii. 不考虑缓存数据的更新,缓存数据不可能成为脏数据(但在软件系统中,往往“不可能”会在一次又一次的重构之后变为“可能”);
iii. 考虑缓存数据的更新,当源数据变化时,实时更新缓存数据。

fragment 2. singleton模式的脏数据问题
/**
* a storage usage handler is used to query the storage usage of users.
*
* @version 1.0 9/9/2003
* @author bill
*/
public class storageusagehandler {
/**
* returns a storageusagehandler instance.
*
* @return the single storageusagehandler instance
*/
public static storageusagehandler getstorageusagehandler() {
if(handler == null) {
handler = new storageusagehandler();
}

return handler;
}

/**
* constructs a storageusagehandler instance.
*/
private storageusagehandler() {
users = context.getallusers();
}

/**
* returns the storage sizes of all the users.
*
* @return the storage sizes
*/
public long[] getsizes() {
long sizes[] = new long[users.size()];

for(int i = 0; i < users.size(); i++) {
sizes[i] = getonesize(users.get(i));
}
}

/**
* returns the storage size of a user.
*
* @param user the specified user
* @return the storage size
*/
protected long getsize(user user) {
// do something...

return 0;
}

/**
* the storageusagehandler singleton.
*/
private static storageusagehandler handler;

/**
* the users.
*/
private list users;
}

您看出了问题所在吗?
fragment 2中,由于没有必要次次实例化storageusagehandler而带来不必要的开销,采用了singleton模式以保证storageusagehandler只被实例化一次。
在实例化sotrageusagehandler时,storageusagehandler的类成员users将被赋值。由于不存在任何对users重新赋值的方法,一直驻留在软件系统中的users将不会发生任何变化。在软件系统启动之后,增加、删除或修改用户的操作经常会发生,而一旦发生这类操作,users就成为了脏数据,fragment 2将无法正常工作。
如何避免使用singleton模式所带来的脏数据问题呢?
对于singleton类的类成员:
i. 对于与singleton类外部无依赖关系的类成员,不存在这种问题;
ii. 对于依赖于singleton类外部的类成员,且该类成员不存在更新机制,最好是将其去掉,需要时从singleton类外部直接获取;如果这种办法不可行,应提供机制以确保在使用该类成员之前,该类成员已经被更新过。

 

 

 

 

 

 

 

 


fragment 3. 类使用的脏数据问题
/**
* a storage usage handler is used to query the storage usage of users.
*
* @version 1.0 9/9/2003
* @author bill
*/
public class storageusagehandler implements adminhandler {
/**
* constructs a storageusagehandler instance.
*/
private storageusagehandler() {
users = context.getallusers();
}

/**
* returns the storage sizes of all users.
*
* @return the storage sizes
*/
public long[] getsizes() {
long sizes[] = new long[users.size()];

for(int i = 0; i < users.size(); i++) {
sizes[i] = getonesize(users.get(i));
}
}

/**
* returns the storage size of a user.
*
* @param user the specified user
* @return the storage size
*/
protected long getsize(user user) {
// do something...

return 0;
}

/**
* displays the storage usage of users.
*
* @param req the http servlet request
* @param res the http servlet response
*
* @throws ioexception
* @throws servletexception
*/
public void process(httpservletrequest req, httpservletresponse res)
throws ioexception, servletexception {

res.setcontenttype("text/html");
res.setheader("cache-control", "no-cache");
res.setheader("pragma","no-cache");
res.setdateheader("expires", 0);

printwriter writer = new printwriter(res.getoutputstream());
long sizes[] = getsizes();
writer.println("");
writer.println("");

for(int i = 0; i < sizes.length; i++) {
writer.print(" ");
writer.print(users.get(i) + ": " + sizes[i]);
writer.println("
");
}

writer.println("");
writer.flush();
writer.close();
}

/**
* the users.
*/
private list users;
}

/**
* an admin servlet as a http servlet to process the admin http servlet
* request and response.
*
* @version 1.0 9/9/2003
* @author bill
*/
public class adminservlet extends httpservlet {
/**
* initiates the configuration.
*
* @param config the servlet config
*
* @throws servletexception
*/
private void initconfig(servletconfig config) throws servletexception {
// do something...

handlermap.put("__storage_usage__", new storageusagehandler());
}

/**
* processes the http servlet request and response.
*
* @throws ioexception
* @throws servletexception
*/
public void service(httpservletrequest req, httpservletresponse res)
throws ioexception, servletexception {

adminhandler handler = handlermap.get(req.getparameter("handler"));

if(handler == null) {
// do something...

return;
}

handler.process(req, res);
}

/**
* the admin handler map(handler name->handler).
*/
private hashmap handlermap = new hashmap();
}

您一定看出了问题所在吧!
fragment 3中,由于storageusagehandler并不遵循singleton模式,尽管storageusagehandler的类成员users只能在实例化storageusagehandler时被赋值,但是在单线程模式下,只要保证每次所使用的storageusagehandler实例是新实例化的,基本上还是没有问题的。
问题在于,在初始化adminservlet的过程中,storageusagehandler被实例化并存储起来。此后,除非servlet container重新装载adminservlet,否则将无法重新实例化storageusagehandler,也将无法更新storageusagehandler的类成员users。这样,在发生了增加、删除或修改用户的操作之后,users将成为脏数据。
如何避免类使用所带来的脏数据问题呢?
i. 对于与类外部无依赖关系的类成员,不存在这种问题;
ii. 对于依赖于类外部的类成员,且该类成员不存在更新机制。最好是将其去掉,需要时从类外部直接获取;如果这种办法不可行,应提供机制以确保在使用该类成员之前,该类成员已经被更新过;如果这种办法还不可行,请清晰地说明类的使用方式,以防止不当的类使用发生。

以上用三个例子列举了三类常见的脏数据问题。事实上,java程序中的脏数据问题存在形式非常多样,因而,在设计、实现、测试和重构过程中,紧记(keep in mind)避免脏数据的存在是非常重要的,我们可以从系统、子系统、类和类成员等各个层次来检查java程序。
“只做好一件事”是对简单性的最佳诠释,这句话同样最好地诠释了软件系统在功能方面的正交性。然而,在面向对象的软件开发过程中,仅仅在功能方面确保正交性是不够的,还应该在数据存储方面来尽量保证正交性。当然,考虑到性能等因素,在数据存储方面确保正交性比较困难,对于破坏此规则的数据存储,应提供机制以确保所使用数据的实时性。


 
 
上一篇: java语言中对hashmap的深度分析与比较    下一篇: java实用技巧:定时执行任务的三种方法
  相关文档
j2me midp 提供的最重要的图形元素 11-17
struts心得--dispatchaction使用日记 11-17
swing读书笔记事件处理 11-17
用java实现一个分页类 11-17
设计模式在ejb中的应用(1) 11-17
学习如何以oo方式创建web页 11-17
think in java 3rd 中文版3 11-17
nan 属性 11-16
win2k下的j2ee配置 11-20
junit实战篇 (一) 11-17
分页javabean 11-17
j2ee基础:j2ee技术中标准组件介绍大全 11-16
struts学习起?i问答 11-17
java学习笔记swingjframe窗口学习 11-17
让界面更加绚丽 java se 6.0 gui体验 11-17
第三方类库--hibernate + proxool配置 01-24
关于spring中的aop的解释 11-17
剖析.net下的数据访问层技术(4) 11-17
探讨 j2se 1.4 发行版中的安全性变化 11-17
java 与.net 在前途上面的比较 11-16
返回首页 | 关于我们 | J网章程 | JSP空间合租 | 客服中心 | 免责声明 | 常见问题 | 参观机房
本站主机空间代理至厦门市华众网络科技有限公司
《中华人民共和国增值电信业务经营许可证》
编号:闽B2-20050079
@2005-2008福建JSP技术网 版权所有 闽ICP备05000928号
厦门(总部):13616026886 福州:0591-87655121
邮箱:admin@fjjsp.com 站长QQ,点击这里给我发消息