java NIO(Channel)
1. NIO与IO的区别
NIO(New IO/Non-Blocking IO)是支持面向缓冲区、基于通道的IO操作。将以更高的效率进行文件的IO操作,可以代替原来的IO操作。
IO | NIO |
---|---|
面向流 | 面向缓冲区 |
阻塞IO | 非阻塞IO |
———— | Selector(多路复用器、选择器) |
2. 通道与缓冲区
通道是建立一个程序与资源之间的通路,即表示打开到IO设备(文件、套接字)的连接,不能通过通道直接操作资源(文件),NIO是以块来操作资源的,所以要借助缓冲区来实现。
通道负责传输,缓冲区负责存储数据,即当做数据的中转站。
3. 缓冲区
分为直接缓冲区,和间接缓冲区。
间接缓冲区:在JVM的堆上建立的缓冲区。
直接缓冲区:直接在系统内存上建立的缓冲区。
4. 通道基础
4.1. 通过与系统内存交互示意图
4.2. 通道的主要实现
-
需要实现的接口:
java.nio.channels.Channel
-
声明:
`public interface Channel
extends Closeable`
- 实现该接口的常用的类:
-
磁盘IO:FileChannel
-
网络IO:
-
SocketChannel(普通套接字通道[TCP])
-
ServerSocketChannel(服务器套接字通道[TCP])
-
DatagramChannel (UDP套接字通道)
5. 通道获取与使用
5.1. 获取通道
- 对支持通道的类提供getChannel方法
-
本地IO:FileInputStream、FileOutputStream、RandomAccessFile
-
网络IO:Socket、ServerSocket、DatagramSocket
- 各个通道提供了一个静态方法open
-
static FileChannel open(Path path, OpenOption... options)
-
static SocketChannel open(SocketAddress remote)
/static SocketChannel open()
-
static ServerSocketChannel open()
-
static DatagramChannel open()
/static DatagramChannel open(ProtocolFamily family)
- 通过Files工具类获取
-
static SeekableByteChannel newByteChannel(Path path, OpenOption... options)
-
其中SeekableByteChannel是FileChannel的父类
5.2. 通道使用
利用通道完成文件复制
5.2.1. 利用间接缓冲区完成
public void tets1() throws Exception {
//获取输入输出的通道
FileChannel fin = FileInputStream("1.txt").getChannel();
FileChannel fout = FileOutputStream("2.txt").getChannel();
//分配指定大小的缓冲区,间接缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//将数据存入缓冲区
while(fin.read(buf) != -1){
//切换到读模式
buf.flip();
//写入
fout.write(buf);
//重置缓冲区,便于下次使用
buf.clear();
}
//关闭通道
fin.close();
fout.close();
}
5.2.2. 利用内存映射文件完成(直接缓冲区)
直接缓冲区
public void test2() throws Exception {
//获取通道
FileChannel fin = FileChannel.open(Paths.get("1.txt"),StandardOpenOption.READ);
FileChannel fout = FileChannel.open(Paths.get("2.txt"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//内存映射
MappedByteBuffer inBuf = fin.map(MapMode.READ_ONLY,0,fin.size());
MappedByteBuffer oBuf = fout.map(MapMode.READ_WRITE,0,fin.size());
//直接对缓冲区进行数据读写操作
Byte[] dst = new Byte[inBuf.limit()];
inBuf.get(dst);
oBuf.put(dst);
fin.close();
fout.close();
}
5.2.3. 通道之间的数据传输
直接缓冲区
public void test3() throws Exception {
FileChannel fin = FileChannel.open(Paths.get("1.txt"),StandardOpenOption.READ);
FileChannel fout = FileChannel.open(Paths.get("2.txt"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//直接利用通道传输
//fin.transformTo(0,fin.size(),fout);
fout.transformFrom(fin,0,fin.size());
fin.close();
fout.close();
}
5.2.4. 分散(Scatter)与聚集(Gather)
-
分散读取(Scattering Read):将通道中的数据按顺序分散到多个缓冲区
-
聚集写入(Gathering Write):将各个缓冲区的数据按顺序聚集到通道中
public void test4() throws Exception{
//获得随机访问文件
RandomAccessFile file = new RandomAccessFile("1.txt","rw");
//获得通道
FileChannel fileChannel = file.getChannel();
//获得指定大小的缓冲区
ByteBuff buf1 = ByteBuff.allocate(100);
ByteBuff buf2 = ByteBuffer.allacate(1024);
ByteBuffer[] bufs = {buf1,buf2};
//分散读到缓冲区
fileChannel.read(bufs);
//将每个缓冲区切换到读模式
for(ByteBuffer buf : bufs){
buf.flip();
}
System.out.println(new String(bufs[0],0,bufs[0].limit()));
System.out.println("============================");
System.out.println(new String(bufs[1],0,bufs[1].limit()));
//聚集写入
RandomAccessFile file2 = new RandomAccessFile("2.txt","rw");
FileChannel fileChannel2 = file2.getChannel();
fileChannel2.write(bufs);
fileChannel.close();
fileChannel2.close();
}