1、Java NIO介绍
- non-blocking IO,从JDK1.4开始Java提供了一系列改进的IO新特性,统称为NIO(New IO),同步非阻塞;
- 三大核心部分:Channel、Buffer、Selector
- NIO是面向缓冲区(或是面向块编程的)。将数据读取到一个稍后处理的缓冲区,需要时可在缓冲区前后移动,增加了处理过程的灵活性;
- NIO的非阻塞模式:使一个线程从某通道发送请求或者读取数据,但是仅能得到目前可用的数据,如果目前没有数据可用时,什么也不会获取,而不是保持线程阻塞,直至数据变得可以读取之前,线程可以做其他事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,不需要等待完全写入,该线程可以去做别的事情;
- NIO可以做到使用一个线程处理多个操作。
- HTTP2.0使用了多路复用技术,做到使用一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级;
2、NIO与BIO比较
- BIO以流的方式处理数据,NIO以块的方式处理数据,块IO的效率比流IO高很多;
- BIO是阻塞的,NIO是非阻塞的;
- BIO基于字节流和字符流进行操作,NIO基于channel和buffer进行操作,数据总是从通道读取到缓冲区,或者从缓冲区写道通道。selector用于监听多个通道的事件,因此使用单个线程就可以监听多个客户端通道;
3、NIO三大核心原理图
- 每个channel对应一个buffer
- selector对应一个线程,一个线程对应多个channel
- 程序切换到哪个channel是由事件决定的;
- selector会根据不同的事件在各个通道上切换;
- buffer就是一个内存块,底层是一个数组;
- 数据的读取写入是通过buffer,BIO中要么是输入流要么是输出流不能双向,但是NIO的buffer可以读可以写,需要flip方法切换;
- channel是双向的;
4、Buffer
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
ByteBuffer
Java的基本数据类型都有一个Buffer类型与之对应,最常用的就是ByteBuffer类;
5、通道
- NIO的通道类似于流,但还有区别
- 通道可以同时进行读写,而流只能读或者只能写
- 通道可以实现异步读写数据
- 通道可以从缓冲读数据,也可以写数据到缓冲;
- BIO的stream是单向的,FileInputStream对象只能读取数据,NIO的channel是双向的;
- channel在NIO中是一个接口;
- 常见的channel:FileChannel、DatagramChannel、ServerSocketChannel、SocketChannel(ServerSocketChannel类似于ServerSocket,SocketChannel类似于Socket);
- FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel用于TCP的数据读写;
5.1 FileChannel类
//从通道读取数据并放到缓冲区中;
public int read(ByteBuffer dst);
//把缓冲区的数据写道通道中
public long read(ByteBuffer[] dsts);
public long transferFrom(ReadableByteChannel src,
long position, long count);
public long transferTo(long position, long count, WritableByteChannel target);
实例1:本地文件写数据
将字符串“hello,world”写入到file1.txt
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIOFileChannel01 {
public static void main(String[] args) throws Exception {
String str="hello,world";
FileOutputStream fileOutputStream=new FileOutputStream("file1.txt");
//通过fileOutputStream获取对应的FileChannel
FileChannel channel = fileOutputStream.getChannel();
//创建一个缓冲区ByteBuffer
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
byteBuffer.put(str.getBytes());
//对byteBuffer进行flip
byteBuffer.flip();
//byteBuffer数据写入到channel
channel.write(byteBuffer);
fileOutputStream.close();
}
}
实例2:本地文件读数据
将file1.txt的数据显示在屏幕上;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIOFileChannel02 {
public static void main(String[] args) throws Exception {
File file=new File("file1.txt");
FileInputStream fileInputStream=new FileInputStream(file);
//获取到fileInputStream对应的channel
FileChannel channel = fileInputStream.getChannel();
//创建缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate((int)file.length());
//将channel的数据读入到缓冲区
channel.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));
fileInputStream.close();
}
}
使用一个buffer完成文件读取、写入
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIOFileChannel03 {
//使用一个buffer完成文件读取写入
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream=new FileInputStream("file1.txt");
FileOutputStream fileOutputStream=new FileOutputStream("file2.txt");
FileChannel channel1 = fileInputStream.getChannel();
FileChannel channel2 = fileOutputStream.getChannel();
ByteBuffer byteBuffer=ByteBuffer.allocate(10);
//循环读取
while(true){
byteBuffer.clear();
int read = channel1.read(byteBuffer);
System.out.println("read="+read);
if(read==-1)break;
byteBuffer.flip();
channel2.write(byteBuffer);
}
fileInputStream.close();
fileOutputStream.close();
}
}
使用transferFrom完成拷贝
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
public class NIOFileChannel04 {
public static void main(String[] args) throws Exception{
FileInputStream fileInputStream=new FileInputStream("me.jpg");
FileOutputStream fileOutputStream=new FileOutputStream("me2.jpg");
FileChannel channel1 = fileInputStream.getChannel();
FileChannel channel2 = fileOutputStream.getChannel();
//使用transferFrom完成拷贝
channel2.transferFrom(channel1,0,channel1.size());
channel1.close();
channel2.close();
fileInputStream.close();
fileOutputStream.close();
}
}
put和get应该是同一个类型
import java.nio.ByteBuffer;
public class NIOByteBufferPutGet {
public static void main(String[] args) {
ByteBuffer buffer=ByteBuffer.allocate(64);
buffer.putInt(100);
buffer.putLong(9);
buffer.putChar('a');
buffer.putShort((short)4);
//取出
buffer.flip();
System.out.println(buffer.getInt());
System.out.println(buffer.getLong());
System.out.println(buffer.getChar());
System.out.println(buffer.getShort());
}
}
普通buffer转成只读buffer
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIOFileChannel01 {
public static void main(String[] args) throws Exception {
String str="hello,world";
FileOutputStream fileOutputStream=new FileOutputStream("file1.txt");
//通过fileOutputStream获取对应的FileChannel
FileChannel channel = fileOutputStream.getChannel();
//创建一个缓冲区ByteBuffer
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
byteBuffer.put(str.getBytes());
//对byteBuffer进行flip
byteBuffer.flip();
//byteBuffer数据写入到channel
channel.write(byteBuffer);
fileOutputStream.close();
}
}
直接在内存中修改文件
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class MappedByteBufferTest {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("file1.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
map.put(0,(byte)'H');
map.put(3,(byte)'9');
map.put(5,(byte)'Y');
randomAccessFile.close();
}
}