Nio学习
——如何以行为单位来读写数据 Victor
最近在学习Nio,想比较出Nio与io之间的效率。确实发现Nio在大部分情况下比io要快、消耗的内存与CPU要小,在处理大数据、多并发的情况下,使用Nio更好。
随着学习的深入,却发现Nio没有按行读取文件的方法。这在某种特殊要求下,无疑限制了Nio的使用,于是我试着自己实现下Nio按行读写的功能。
首先,实现读取一行的功能,具体代码如下:
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* Use for reader line from file by use Nio
*
* @author Victor
*/
public class NioReader {
private static final String lineSeparator = "\r\n";//换行关键字
private final int size = 1024 * 8;//块的大小
private static int n;//该文件可分成块数量
private FileInputStream fin;//读取文件的流
private FileChannel fcin;//管道
private static int count = 0;//记录该块读取到第几行
private static int piece = 0;//记录读取到哪一块
private static String[] strs;//该块转换后的数组,一行数据对应一个元素
public NioReader(String path)
throws IOException {
fin = new FileInputStream(path);
fcin = fin.getChannel();
long length = fcin.size();
n = (int)length / size;
strs = getStrsBySize();
}
public String getNextLine()
throws IOException {
String s = null;
int lengh = strs.length - 1;
if (count < lengh) {
s = strs[count];
count++;
} else if (count == lengh) {
if (piece > n) {
s = strs[count];
count++;
} else {
strs = getStrsBySize();
s = getNextLine();
}
}
return s;
}
/**
* 获取下一块的String数组
*/
private String[] getStrsBySize()
throws IOException {
count = 0;
if (piece <= n) {
long opint = piece * size;
MappedByteBuffer buffer;
byte[] content = new byte[size];
if (piece == n) {
long length = fcin.size();
int finalsize = (int)(length - opint);
buffer = fcin.map(FileChannel.MapMode.READ_ONLY, opint,
finalsize);
buffer.get(content, 0, finalsize);
} else {
buffer = fcin.map(FileChannel.MapMode.READ_ONLY, opint,
size);
buffer.get(content, 0, size);
}
String str;
if (piece == 0) {
str = new String(content);
str.trim();
} else {
String newStr = new String(content);
newStr.trim();
str = strs[strs.length - 1] + newStr;
}
strs = str.split(lineSeparator);
piece++;
}
return strs;
}
public void close()
throws IOException {
if (fin != null) {
fin.close();
}
if (fcin != null) {
fcin.close();
}
}
}
然后实现写一行的功能:
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NioWriter {
private static final String lineSeparator = "\r\n";
private FileOutputStream fout;
private FileChannel fcout;
private ByteBuffer buffer;
public NioWriter(String path) throws IOException {
fout = new FileOutputStream(path);
fcout = fout.getChannel();
}
public void putln(String s) {
if (fcout == null) {
throw new IllegalArgumentException(
"attempt to use a closed Writer");
}
buffer= ByteBuffer.allocate(1024*8);
buffer.clear();
s = s.trim() + lineSeparator;
byte[] bytes = s.getBytes();
buffer.put(bytes);
buffer.flip();
try {
fcout.write(buffer);
fout.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public void close() throws IOException {
if (fout != null) {
fout.close();
}
if (fcout != null) {
fcout.close();
}
}
}
接下来就是测试了,为了方便测试它的内存消耗,我还特意写个测试内存的工具类:
public class RuntimeMemory {
private static final int MB = 1024 * 1024;
public static DecimalFormat df = new DecimalFormat("#####.##");
public static void getMemory() {
Runtime run = Runtime.getRuntime();
long max = run.maxMemory();
long total = run.totalMemory();
long free = run.freeMemory();
long usable = max - total + free;
StringBuffer sb = new StringBuffer();
sb.append("[RuntimeMemory] Max :").append(getMb(max)).append(
" Assigned :").append(getMb(total)).append(" Free :").append(
getMb(free)).append(" Usable :").append(getMb(usable));
System.out.println(sb.toString());
}
private static String getMb(long memory) {
double mb = (double)memory / MB;
return "[" + df.format(mb) + " MB]; ";
}
}
主测试类:
public class TestNio {
/**
* @author Victor
* @throws IOException
*/
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
RuntimeMemory.getMemory();
String infile = "D:\\test.txt";
String outfile = "D:\\copy.txt";
NioReader reader=new NioReader(infile);
NioWriter writer=new NioWriter(outfile);
String line;
while ((line = reader.getNextLine()) != null) {
try {
if ((line == null) || line.trim().equals("")) {
continue;
}
writer.putln(line);
} catch (Exception ex) {
ex.printStackTrace();
}
}
reader.close();
writer.close();
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
RuntimeMemory.getMemory();
}
}
当然为了对比,我另外还写了普通io的测试,在此就不展示代码了,下面是我对二者测试的结果分析:
1. 当数据量为7M时:
Nio:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.4 MB]; Usable :[63.02 MB];
1201ms 1357ms 1279ms 1295ms 1404ms (运行了多次)
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.5 MB]; Usable :[63.13 MB];
Io:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.39 MB]; Usable :[63.02 MB];
281ms 234ms 249ms 265ms 234ms
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[3.77 MB]; Usable :[62.4 MB];
2. 当数据量为370M时:
Nio:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.4 MB]; Usable :[63.02 MB];
55799ms
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[3.31 MB]; Usable :[61.94 MB];
Io:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.39 MB]; Usable :[63.02 MB];
9882ms
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.39 MB]; Usable :[63.02 MB];
一些小数据也测试过,但差别不大,就不列举了。
此外,对于CPU的消耗,在运行时,我进行了监测,相对来说使用Nio所消耗的CPU较大些。
从上面的数据可以分析出:
用Nio来以行为单位读写数据的速度是普通Io的5-6倍(汗!!!可能我写的代码并不完善),虽然Nio在内存的消耗上占据一点优势,但不明显,消耗的CPU却更多。
宗上,在需要以行为单位来读写数据时,还是得采用普通IO。。。。
再说说测试中所遇到的问题:
首先,就是reader的时候,因为它是按指定的大小拆分块,有可能会把一行拆分到不同的块中,所以我采取的策略就是保留每块转换成数组后的最后一个元素,添加到下一个块中。
然后,就是在写数据的时候,要注意ByteBuffer的大小,由于我没有txt的大数据,采用了一个数据库的脚本,结果运行时老是报缓冲区溢出的异常,找了很久。。。才发现是被复制的文本有问题:那个sql中有些行竟然有将近1M的大小,哎!
最后,再说一些还没有解决的问题,就是在更大的数据拷贝完成后,悲催的发现,拷贝后的文件竟然比源文件要大一些(源文件500M的样子,复制的文件多出了5-6k,由于数据大多了,无法仔细分析)!
(新手!仅供参考!求前辈指点!)
分享到:
相关推荐
### Java NIO 处理超大数据文件的知识点详解 #### 一、Java NIO简介 Java NIO(New IO)是Java平台上的新输入/输出流API,它提供了与传统IO(即Java IO)不同的数据处理方式。NIO在Java 1.4版本引入,并在后续版本...
自Java 1.4版本引入NIO后,它为Java开发者提供了更高效的数据传输方式,尤其是在处理大量并发连接时。NIO的核心在于通道(Channels)和缓冲区(Buffers)的概念,与传统的流(Streams)有所不同。 1. **通道...
8. **聚合操作(Scatter/Gather)**:NIO支持数据的分散读取(Scatter)和聚集写入(Gather),即可以从多个缓冲区分散读取数据,也可以将数据聚集到多个缓冲区进行写入。 9. **文件锁(File Locking)**:NIO提供...
同时,Buffer的使用也非常重要,学会如何正确地读写数据,以及如何有效地管理缓冲区。 总的来说,NIO为Java开发者提供了更高效的I/O处理能力,尤其在处理高并发和大流量场景时,它的优势更加明显。然而,NIO的学习...
- 读写数据:使用通道与缓冲区交互,将数据从通道读入缓冲区,或者将缓冲区中的数据写入通道。 - 注册选择器:将通道注册到选择器,指定感兴趣的事件类型。 - 监控事件:调用选择器的select方法,获取已准备就绪...
NIO在Java 1.4版本引入,提供了更高效的数据处理和通道通信方式,特别适用于高并发、大数据量的系统。Netty是一个基于NIO的高性能、异步事件驱动的网络应用框架,它简化了网络编程,广泛应用于服务器端应用开发。 ...
标准 IO 以流的方式处理数据,也就是说数据是以流的形式传输的,而 NIO 则以块的方式处理数据。这种差异对数据的传输和处理方式产生了很大的影响。在标准 IO 中,数据是以流的形式读取和写入的,而 NIO 则将数据读取...
NIO(New Input/Output)是Java提供的一种非阻塞I/O模型,它极大地提高了系统在处理大量数据时的性能。本篇文章将带你深入理解NIO的源码,揭示其在大数据环境下的应用与优势。 NIO与传统的IO模型(-blocking I/O)...
- **通道(Channel)**:在NIO中,数据的读写都是通过通道进行的。通道类似于流,但具有双向性,可以同时进行读写操作。常见的通道类有FileChannel、SocketChannel、ServerSocketChannel和DatagramChannel等。 - **...
本篇内容将深入讲解NIO的核心概念以及基本的读写操作。 ### NIO核心概念 1. **通道(Channel)**:通道是NIO中的关键组件,它连接到I/O设备(如文件、网络套接字等)。通道可以读取或写入数据,且是双向的。例如,...
- **Buffers**:Buffers是NIO中的数据容器,用于存储从Channel读取的数据或将数据写入Channel。Java NIO支持多种类型的Buffer,包括: - **ByteBuffer**:用于存储字节数据。 - **CharBuffer**:用于存储字符数据...
- Channel是数据传输的通道,它可以连接到I/O设备(如文件、套接字等)并读写数据。Java NIO提供了多种类型的通道,如FileChannel、SocketChannel、ServerSocketChannel等。通道是双向的,既可读又可写。 4. **...
传统的IO模型,如描述中的“阻塞I/O”,在读写数据时会一直等待数据的到达,这可能导致资源的浪费和效率的降低。NIO则提供了更加高效和灵活的解决方案。 NIO的核心概念包括: 1. **通道(Channel)**:通道是NIO中...
Java NIO(New IO)是Java 1.4版本引入的一个新模块,它提供了一种新的方式来处理I/O操作,相比传统的IO流,NIO提供了更高效、更灵活的数据读写方式。在这个主题中,我们将深入探讨Java NIO如何用于写文件,特别是在...
《NIO入门》一书是理解Java NIO(New Input/Output)的重要参考资料,NIO在Java编程中扮演着至关重要的角色,特别是在处理高并发、大数据传输等场景下。本PDF文档将引领读者深入理解这一核心概念。 NIO,全称New ...
总的来说,这个实例展示了如何在实际环境中利用编程语言(这里是Java)与服务器进行交互,进行数据的读写操作。了解这些技术对于开发Web应用或进行服务器管理至关重要。在实践中,我们还需要考虑更多的因素,如性能...
3. **Buffer(缓冲区)**:在NIO中,数据读写都是通过缓冲区进行的。缓冲区是一个可以容纳特定类型数据(如字节、字符、整数等)的容器,它提供了对数据的高效访问和管理。 4. **FileChannel**:用于文件的读写,...
- **Buffer的创建和操作**:可以通过allocate()方法创建Buffer,然后使用put()和get()方法读写数据。Buffer的clear()、flip()和rewind()方法用于调整position和limit状态,以适应读写需求。 - **Channel的打开和...