本文目录一览:
- 1、java nio bytebuffer文件读写问题
- 2、真正理解NIO
- 3、NIO的使用介绍
- 4、io和nio的文件读取方式的不同
- 5、Java中nio与普通io有什么优势?
- 6、java如何合并多个大的txt文件?(每个文件50M)。nio处理文件如何提高速度?
java nio bytebuffer文件读写问题
JDK1.4以后就提供java.nio的包,nio主要提供字节与字符的映射、内存映射文件和文件加锁机制
其中内存映射文件在读取大文件时可能会用上,因为内存映射不是直接把文件加载到JVM内存空间
而是借用操作系统对文件的读取,这经历了由当前Java态进入到操作系统内核态,再由操作系统读取文件,
并返回数据到当前Java态的过程。由Java态进入操作系统内核态离不开nio包中两个重要的类
FileChannel 和 ByteBuffer。FileChannel表示文件通道,可以从FileInputStream、FileOutputStream
以及RandomAccessFile对象获取文件通道,你可以从文件通道直接读取文件,也可以使用“内存映射”
即使用通道,将文件内存映射到ByteBuffer,可以映射一部分内容,也可以映射全部内容,使用内存映射
能大幅提高我们操作大文件的速度
FileChannel 和 ByteBuffer文件读取
[java] view plain copy
package nio;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/**
*
* Channel类似与流,数据可以从Channel读取到Buffer,也可以从Buffer写入到Channel
* 但通道和流还是有区别,比如流只能是单向读或写,而通道可以异步读写
*
* @author yli
*/
public class FileChannelTest {
真正理解NIO
一个使用传统阻塞I/O的系统,如果还是使用传统的一个请求对应一个线程这种模式,一旦有高并发的大量请求,就会有如下问题:
1、线程不够用, 就算使用了线程池复用线程也无济于事;
2、阻塞I/O模式下,会有大量的线程被阻塞,一直在等待数据,这个时候的线程被挂起,只能干等,CPU利用率很低,换句话说,系统的吞吐量差;
3、如果网络I/O堵塞或者有网络抖动或者网络故障等,线程的阻塞时间可能很长。整个系统也变的不可靠;
java.nio全称java non-blocking IO(实际上是 new io),是指JDK 1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供 缓存 支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。-nio写入文件速度
HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。
原有的 IO 是面向流的、阻塞的,NIO 则是面向块的、非阻塞的。
java1.4以前的io模型,一连接对一个线程。
原始的IO是面向流的,不存在缓存的概念。Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区-nio写入文件速度
Java IO的各种流是阻塞的,这意味着当一个线程调用read或 write方法时,该线程被阻塞,直到有一些数据被读取,或数据完全写入,该线程在此期间不能再干任何事情了。
NIO是面向缓冲区的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性。
Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。-nio写入文件速度
通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有10000个请求过来,根据实际情况,可以分配50或者100个线程来处理。不像之前的阻塞IO那样,非得分配10000个。
在标准IO API中,你可以操作字节流和字符流,但在新IO中,你可以操作通道和缓冲,数据总是从通道被读取到缓冲中或者从缓冲写入到通道中。
NIO核心API Channel, Buffer, Selector
NIO的通道类似于流,但有些区别如下:
1. 通道可以同时进行读写,而流只能读或者只能写
2. 通道可以实现异步读写数据
3. 通道可以从缓冲读数据,也可以写数据到缓冲:
缓冲区本质上是一个可以写入数据的内存块,然后可以再次读取,该对象提供了一组方法,可以更轻松地使用内存块,使用缓冲区读取和写入数据通常遵循以下四个步骤:
1. 写数据到缓冲区;
2. 调用buffer.flip()方法;
3. 从缓冲区中读取数据;
4. 调用buffer.clear()或buffer.compat()方法;
当向buffer写入数据时,buffer会记录下写了多少数据,一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式,在读模式下可以读取之前写入到buffer的所有数据,一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。-nio写入文件速度
Buffer在与Channel交互时,需要一些标志:
buffer的大小/容量 - Capacity
作为一个内存块,Buffer有一个固定的大小值,用参数capacity表示。
当前读/写的位置 - Position
当写数据到缓冲时,position表示当前待写入的位置,position最大可为capacity – 1;当从缓冲读取数据时,position表示从当前位置读取。
信息末尾的位置 - limit
在写模式下,缓冲区的limit表示你最多能往Buffer里写多少数据; 写模式下,limit等于Buffer的capacity,意味着你还能从缓冲区获取多少数据。
下图展示了buffer中三个关键属性capacity,position以及limit在读写模式中的说明:
缓冲区常用的操作
向缓冲区写数据:
1. 从Channel写到Buffer;
2. 通过Buffer的put方法写到Buffer中;
从缓冲区读取数据:
1. 从Buffer中读取数据到Channel;
2. 通过Buffer的get方法从Buffer中读取数据;
flip方法:
将Buffer从写模式切换到读模式,将position值重置为0,limit的值设置为之前position的值;
clear方法 vs compact方法:
clear方法清空缓冲区;compact方法只会清空已读取的数据,而还未读取的数据继续保存在Buffer中;
一个组件,可以检测多个NIO channel,看看读或者写事件是否就绪。
多个Channel以事件的方式可以注册到同一个Selector,从而达到用一个线程处理多个请求成为可能。
Thanks for:
学习NIO
美团wiki-NIO
NIO的使用介绍
BIO是基于字节流和字符流进行操作的,而NIO是基于通道和缓冲区进行操作的,数据总是从通道中读取到缓冲区,或者从缓冲区写入到通道中。
BIO是阻塞式的,而NIO是非阻塞式的,读写的效率要更加地高。
Channel有点类似BIO中的流的概念,但是比流要强大地多,主要体现在以下方面:
Channel有以下几个基本类型:
关于Channel的使用,给出如下一个简单的示例,将test1中的内容使用channel和buffer拷贝到test2文件中:
除此之外,我们还可以直接将数据从一个channer传输到另外一个channel中。
buffer本质上是一块可以读写数据的缓存,一般遵循以下的使用步骤:
在buffer中有如下几个重要的属性需要掌握:
Buffer基本类型:
这些类型基本上涵盖了你能通过IO发送的数据类型。
channel和buffer之间的读写关系并不是严格的一对一关系,可以通过scatter方式将一个channel中的数据写入多个buffer之中,也可以通过gather方式将多个buffer中的数据读取到一个channel中。-nio写入文件速度
一个Selector线程能注册多个Channel,并对它们进行(非)阻塞式地监听,任意一个Channel有事件就绪,Selector就将该事件交给对应的业务线程进行处理,Selector继续对注册的Channel进行监听。-nio写入文件速度
我们这里以服务端的一个实现来介绍Selector多路复用器的使用:
在实际的开发实践中,很少有人直接使用NIO,因为它使用起来比较复杂,而且在linux上还存在Bug,比如epoll的空轮询导致CPU100%的问题;而且重复造轮子会导致开发效率底下,无法保证程序的质量,所以大家都倾向于使用基于NIO实现的中间件工具,比如Netty。-nio写入文件速度
io和nio的文件读取方式的不同
io,也称old io,读取文件主要通过流,从磁盘上一个一个字符的读,效率比较低下。
nio,在对文件操作下改进了方式,通过块读取,一整块一整块的读取,所以读取出来的不会是一个字符,而是一个块,把这些数据放到内存缓冲区内。在进行操作。通过块的读取来提高速度。(块操作,fileChannel)-nio写入文件速度
内存映射,MappedByteBuffer,这个主要是通过内存映射,即利用虚拟内存(把文件某一部分当成内存),直接操作文件,对于一些要在内存中大块操作的文件,比如1G的文件
你要在内存中操作200M的部分,,把200M读到物理内存是比较耗内存和CPU的,不如直接把那部分文件虚拟成内存直接操作。
下面是对用以上三个文式对一个300M的文件进行读取,并写入另一个文件的测试:
1. inputstream CPU5.6% 内存2.6M costTime:4.039S 使用缓冲区byte:4kb-nio写入文件速度
2. filechannel CPU1.3% 内存2.6M costTime:3.359S 使用缓冲区byte:0(transferTo方式)-nio写入文件速度
3. MappedByteBuffer CPU6.9% 内存2.4M costTime:6.966S 内存映射:300M
可见对大文件块读取是最好的;如果要直接操作文件的很大的一部分的内容,则比较适合MappedByteBuffer ;如读取很小的内容,比如8B的内容,inputStream可能是最好的。
代码如下,选几个文件自已试下。使用jdk自带JConsole进行观察。
public class Test {
public static void main(String[] args)throws Exception{
File ff=new File("F:/workspace/pureMQ/src/mq/pure/file/cc.rar");
File[] f=new File[7];
File[] f_write=new File[7];
Thread.sleep(15000);
for(int i=0;i7;i++){
f[i]=new File("F:/workspace/pureMQ/src/mq/pure/file/1"+(i+1)+".jpg");
}
for(int i=0;i7;i++){
f_write[i]=new File("F:/workspace/pureMQ/src/mq/pure/file/"+i+".jpg");
if(!f_write[i].exists()){
f_write[i].createNewFile();
}
}
f[0]=ff;
long begin=System.currentTimeMillis();
int choice=1;
for(int i=0;i7;i++){
switch(choice){
case 1:
System.out.println("1");
InputStream ips=new FileInputStream(f[i]);
OutputStream ops=new FileOutputStream(f_write[i]);
byte[] b=new byte[4096];
int index;
while((index=ips.read(b))!=-1){
ops.write(b);
}
if(ips!=null){
ips.close();
}
if(ops!=null){
ops.close();
}
break;
case 2:
System.out.println("2");
RandomAccessFile raf=new RandomAccessFile(f[i],"r");
RandomAccessFile raf_write=new RandomAccessFile(f_write[i],"rw");
FileChannel fc=raf.getChannel();
FileChannel fc_write=raf_write.getChannel();
fc.transferTo(0, fc.size(), fc_write);
fc.close();
fc_write.close();
raf.close();
raf_write.close();
break;
case 3:
System.out.println("3");
RandomAccessFile raf2=new RandomAccessFile(f[i],"r");
RandomAccessFile raf2_write=new RandomAccessFile(f_write[i],"rwd");
FileChannel fc2=raf2.getChannel();
FileChannel fc2_write=raf2_write.getChannel();
MappedByteBuffer mbb=fc2_write.map(MapMode.READ_WRITE, 0, fc2.size());
ByteBuffer bb=ByteBuffer.allocate(4096);
while(fc2.read(bb)!=-1){
bb.flip();
mbb.put(bb);
bb.compact();
}
mbb.force();
fc2.close();
fc2_write.close();
raf2.close();
raf2_write.close();
break;
default:
break;
}
}
long end=System.currentTimeMillis();
System.out.println("all time cost:"+ (end-begin));
}
}
Java中nio与普通io有什么优势?
1,nio的主要作用就是用来解决速度差异的。举个例子:计算机处理的速度,和用户按键盘的速度,这两者的速度相差悬殊。
2,如果按照经典的方法:一个用户设定一个线程,专门等待用户的输入,无形中就造成了严重的资源浪费,每一个线程都需要珍贵的cpu时间片,由于速度差异造成了在这个交互线程中的cpu都用来等待。
3,传统的阻塞式IO,每个连接必须要开一个线程来处理,并且没处理完线程不能退出。
4,非阻塞式IO,由于基于反应器模式,用于事件多路分离和分派的体系结构模式,所以可以利用线程池来处理。事件来了就处理,处理完了就把线程归还。
5,而传统阻塞方式不能使用线程池来处理,假设当前有10000个连接,非阻塞方式可能用1000个线程的线程池就搞定了,而传统阻塞方式就需要开10000个来处理。如果连接数较多将会出现资源不足的情况。非阻塞的核心优势就在这里。-nio写入文件速度
java如何合并多个大的txt文件?(每个文件50M)。nio处理文件如何提高速度?
这种情况java.io, nio没有大区别
byte[] buf = new byte[8 * 1024];
try (OutputStream out = new new FileOutputStream(outfile)) {
for (File f : txtFiles) {
try (FileInputStream in = new FileInputStream(f)) {
org.apache.commons.io.IOUtils.copyLarge(in, out, buf);
}
}
}
要是linux下,shell里直接执行cat *.txt out.txt就可以,不用写代码