荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: icefire (......), 信区: Java
标 题: [转载] 提高Java I/O性能的两个技巧
发信站: BBS 荔园晨风站 (Sun Jan 31 20:03:15 1999), 转信
【 以下文字转载自 Program 讨论区 】
【 原文由 Peter 所发表 】
由于JDK1.0.2提供的Java.io包设计不周,Java的输入/输出已成为
限制众多Java应用的瓶颈。问题的关键是java.io中的大多数类都没
有缓冲区。实际上,有缓冲区的类只有Buffe redInputStream和Buffe
redOutputStream,但是它们提供的方法非常有限。例如,在许多与文
件相关的应用中,需要逐行对文件进行语法分析,但是只有DataInputS
tream类支持readLine 方法,这个类没有内置的缓冲区,readLine方法
实际上是以字符"\n"或"\r\n"为一行的界定符,一个字符一个字符地
读数据流。每次的字符读取操作使文件的输入/输出变得复杂,尤其是
读一个大文件时I/O效率更低,在没提供缓冲区的情况下,操作一个五
百万字节的文件至少要读取五百万次字符。
新的JDK1.1增加了一批Reader类和Writer类来提高Java的输入/
输出性能。在处理大文件时,BufferedReader类支持的readLine方法
比DataInputStream类支持的readLine方法至少要快10到20倍。遗憾
的是,JDK1.1并不能解决所有的性能问题。例如,在要对一个大文件进
行语法分析却不想将它读入内存的情况下,RandomAccessFile是一个
很有用的类,但JDK
1.1仍没有为它设置缓冲区,又没有提供相应的Reader类。处理此
类I/O问题时,JDK1.1就显出它效率低下的缺憾。
如何处理与缓冲区有关的I/O问题
处理文件I/O效率低下的问题,我们需要一个有内置缓冲区的Rand
omAccessFile类。为了能继承此类中所有的方法,我们定义一个Rando
mAccessFile类的子类,命名为Braf(Buffered randomaccessfile)。
public class Braf extends RandomAccessFile{
}
为了提高效率,我们定义一个byte型缓冲区而不是char型缓冲区
。变量buf—end、buf—pos和real—pos被用来记录缓冲区中的有效
地址:
byte buffer[];
int buf—end=0;
int buf—pos=0;
long real—pos=0;
补充一个用来说明缓冲区大小的参数,重写Braf类的构造方法:
public Braf(String filename,String mode,int bufsize)thro
ws IOException{
super(filename,mode);
invalidate();
BUF—SIZE=bufsize;
buffer=new byte[BUF—SIZE];
}
重载父类的read方法,使新的read方法总是首先从缓冲区中读取
数据,直到缓冲区中的空间用完,才调用父类中的read方法。因为当缓
冲区空间用尽时,新的read方法会调用fillBuf fer方法来填充缓冲区
。在方法fillBuffer中,调用父类本身的read方法。访问权限private
的invalidate方法被用来指示缓冲区不再容纳有效内容。当seek方
法把文件指针移出缓冲区时,invalidate方法的指示就显得非常必要
。
public final int read() throws IOException{
if(buf—pos >=buf—end) {
if(fillBuffer() < 0)
return -1;
}
if(buf—end == 0) {
return-1;
}else{
return buffer[buf—pos++];
}
}
private int fillBuffer() throws IOException{
int n=super.read(buffer,0,BUF—SIZE);
if(n>=0){
real—pos +=n;
buf—end = n;
buf—pos = 0;
}
return n;
}
private void invalidate() throws IOException{
buf—end = 0;
buf—pos = 0;
real—pos=super.getFilePointer();
}
重载另一个带有参数的read方法。若是有足够的缓冲区,新的rea
d方法将简单调用Syst em.arraycopy,把部分缓冲区直接拷贝到用户
区。由于在代替readLine方法的getNextLine方法中,read方法得到充
分利用,所以使Java的I/O性能极大地提高。新read方法的代码如下:
public int read(byte b[],int off,int len)throws IOExcept
ion{
int leftover=buf—end-buf—pos;
if(len <= leftover) {
System.arraycopy(buffer,buf—pos,b,off,len);
buf—pos +=len;
return len;
}
for(int i=0; i<len; i++){
int c=this.read();
if(c !=-1)
b[off+i〗=(byte)c;
else{
return i;
}
}
return len;
}
为了充分利用缓冲区给我们带来的便利,最初的getFilePointer
方法和seek方法也需要重载,重载后的新方法大部分时间在缓冲区进
行操作,实现更为简单。
public long getFilePointer() throws IOException{
long l = real—pos;
return (l-buf—end+buf—pos);
}
public void seek(long pos)throws IOException{
int n=(int)(real—pos—pos);
if(n>=0 && n<=buf—end) {
buf—pos=buf—end-n;
}else{
super.seek(pos);
invalidate();
}
}
需要着重指出的是,因为父类中readLine方法被定义成final,我
们不能对它进行简单的重载,而是用一个新方法getNextLine方法,来
代替readLine方法。getNextLine方法首先判断缓冲区里是否有未读
取的内容,如果没有,就要填充缓冲区。如果在缓冲区中有未读取的内
容并找到新行的界定符,就从缓冲区中读取一个新行,并把它转换成St
ring。否则,就简单地调用read方法一个字节一个字节地读取。虽然
后一部分的代码与父类中的readLine方法相类似,但因为新类中的rea
d方法中用到了缓冲区,就使Java的I/O性能要好得多。
/*
**return a next line in string
*/
public final string getNextLine() throws IOException
{
String str=null;
if(buf—end-buf—pos<=0) {
if(fillBuffer()<0){
throw new IOException("error in filling buffer!");
}
}
int lineend = -1;
for(int i=buf—pos;i<buf—end; i++){
if(buffer[i]==‘\n’){
lineend=i;
break;
}
}
if(lineend < 0){
StringBuffer input=new StringBuffer(256);
int c;
while(((c=read())!=-1)&&(c !=‘\n’)){
input.append((char)c);
}
if((c==-1)&&(input.length()==0)) {
return null;
}
return input.toString();
}
if(lineend > 0 && buffer[lineend-1]==‘\r’)
str=new String(buffer,0,buf—pos,lineend-buf—pos -1)
;
else
str=new String(buffer,0,buf—pos,lineend-buf—pos);
buf—pos=lineend+1;
return str;
}
经试验,需要对大型文件进行逐行语法分析时,新类Braf的性能比
RandomAccessFile类至少提高了25倍。
本文所阐述的方法也适用于那些文件I/O操作较集中的地方。
关闭Synchronization
除了上面讨论的输入/输出问题外,降低Java效能的另一个因素是
synchronized语句体。通常,一个synchronized方法的开支大约是通
常方法的6倍。若应用程序不是多线程的,或者在应用程序的某一部分
中只有一个线程执行,就不需要任何并发的部分。当前,Java中还没有
关闭synchronization的方法。一个简单的办法是获取类的源代码,移
去synchronized语句体,并生成一个新类。例如,在类BufferedInputS
tream中,两个read方法是并发的,而所有别的I/O方法是建立在它们的
基础之上的。可以简单地将类名改为NewBIS,例如,从JavaSoft的JD K
1.1提供的BufferedInputStream.java中拷贝这个类的源代码,从NewB
IS.java中移去synch ronized语句体,重新编译NewBIS。
--
※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.1.3]
--
※ 转载:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.0.26]
[回到开始]
[上一篇][下一篇]
荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店