欢迎大家来到IT世界,在知识的湖畔探索吧!
Java NIO 基本介绍
1、Java NIO 全称Java non-blocking IO,是指JDK提供的新API。从JDK1.4开始,Java提供了一系列改进的输入/输出的新特性,被统称为NIO(即New IO),是同步非阻塞的。
2、NIO相关类都被放在Java.nio包及子包下,并且对原Java.io包中的很多类进行改写。
3、NIO有三大核心部分:Channel(通道),Buffer(缓冲区),Selector(选择器);
4、NIO是面向缓冲区,或者面向块编程的,数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就提高了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩网络。
5、Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可用读取之前,该线程可用继续做其他事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
public class BasicBuffer {
public static void main(String[] args) {
//举例说明Buffer的使用(简单说明)
//创建一个Buffer,大小为5,即可以存放5个int
IntBuffer intBuffer = IntBuffer.allocate(5);
//向buffer存放数据
for(int i=0;i<intBuffer.capacity();i++){
intBuffer.put(i*2);
}
//如何从buffer读取数据
//将buffer转换,读写切换
intBuffer.flip();
while (intBuffer.hasRemaining()){
System.out.println(intBuffer.get());
}
}
}
欢迎大家来到IT世界,在知识的湖畔探索吧!
6、通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有10000个请求过来,根据实际情况,可以分配50或者100个线程来处理。不像之前的阻塞IO那样,非得分配10000个。
7、HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.0大了好几个数量级。
NIO和BIO的比较
1、BIO以流的方式处理数据,而NIO以块的方式处理数据,块I/O的效率比流I/O高很多;
2、BIO是阻塞的,NIO则是非阻塞的;
3、BIO基于字节流和字符流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据达到等),因此使用单个线程就可以监听多个客户端通道。
NIO三大核心原理示意图
Selector、Channel和Buffer的关系图(简单版)
1、每个channel都会对应一个Buffer;
2、Selector对应一个线程,一个线程对应多个channel(连接);
3、该图反应了有三个channel注册到该selector;
4、程序切换到哪个channel是由事件决定的,Event就是一个重要的概念;
5、Selector会根据不同的事件,在各个通道上切换;
6、Buffer就是一个内存块,底层是有一个数组;
7、数据的读取写入是通过Buffer,这个和BIO是不同的,BIO中要么是输入流,要么是输出流,不能双向,但是NIO的Buffer是可以读也可以写,需要flip方法切换;
8、channel是双向的,可以返回底层操作系统的情况,例如:Linux,底层的操作系统通道就是双向的。
缓冲区(Buffer)
缓冲区(Buffer):缓冲区本质上是一个可以读写数据的内存块,可以理解成是一个容器对象(含数组),该对象提供了一组方法,可以更轻松地使用内存块,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,Channel提供从文件、网络读取数据的渠道,但是读取或写入数据都必须经由Buffer,如图:
在NIO中,Buffer是一个顶层父类,它是一个抽象类,类的层级关系图如下:
Buffer类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息
Buffer类相关方法一览
ByteBuffer:从前面可以看出对于Java中的基本数据类型(Boolean除外),都有一个Buffer类型与之相对应,最常用的自然是ByteBuffer类(二进制数据),该类的主要方法如下:
通道(Channel)
1、NIO的通道类似于流,但有些区别如下:
① 通道可以同时进行读写,而流只能读或者只能写;
② 通道可以实现异步读写数据;
③ 通道可以从缓冲读数据,也可以写数据到缓冲;
2、BIO中的stream是单向的,例如FileInputStream对象只能进行读取数据的操作,而NIO中的通道(Channel)是双向的,可以读操作,也可以写操作。
3、Channel在NIO中是一个接口 public interface Channel extends Closeable{};
4、常用的Channel类有:FileChannel、DatagramChannel、ServerSocketChannel和SocketChannel。【ServerSocketChannel类似ServerSocket,SocketChannel类似Socket】
5、FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel用于TCP的数据读写。
FileChannel类
FileChannel主要用来对本地文件进行IO操作,常见的方法有
1、public int read(ByteBuffer dst),从通道中读取数据并放到缓冲区中;
2、public int write(ByteBuffer src),把缓冲区的数据写到通道中;
3、public long transferFrom(ReadableByteChannel src,long position,long count ),从目标通道中复制数据到当前通道。
4、public long transferTo(long position,long count,WritableByteChannel target )把数据从当前通道复制到目标通道。
案例演示
案例一:本地文件写数据
实例要求:
1、使用前面学习后的ByteBuffer(缓冲)和FileChannel(通道),将“hello world”写入到file01.txt中;
2、文件不存在则创建
欢迎大家来到IT世界,在知识的湖畔探索吧!public class NIOFileChannel01 {
public static void main(String[] args) throws Exception{
String str = "hello,world";
//创建一个输出流-》channel
FileOutputStream fileOutputStream = new FileOutputStream("d://file01.txt");
//通过fileOutputStream获取对应的FileChannel
//这个fileChannel真实类型是 FileChannelImpl
FileChannel fileChannel = fileOutputStream.getChannel();
//创建一个缓冲区 ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//将str放入byteBuffer
byteBuffer.put(str.getBytes());
/**
* 对byteBuffer 进行flip,即让position指向开始位置
* public final Buffer flip() {
* limit = position;
* position = 0;
* mark = -1;
* return this;
* }
*/
byteBuffer.flip();
//将byteBuffer数据写入到fileChannel
fileChannel.write(byteBuffer);
fileOutputStream.close();
}
}
案例二:本地文件读数据
1、使用前面学习后的ByteBuffer(缓冲)和FileChannel(通道),将file01.txt中的数据读入到程序,并显示在控制台屏幕;
2、假定文件已经存在;
public class NIOFileChannel02 {
public static void main(String[] args) throws Exception{
//创建文件的输入流
File file = new File("d:\\file01.txt");
FileInputStream fileInputStream = new FileInputStream(file);
//通过fileInputStream获取对应的FileChannel->实际类型 FileChannelImpl
FileChannel fileChannel = fileInputStream.getChannel();
//创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
//将通道的数据读入到Buffer
fileChannel.read(byteBuffer);
//将byteBuffer的字节数据转成字符串
System.out.println(new String(byteBuffer.array()));
fileInputStream.close();
}
}
案例三:使用一个Buffer完成文件读取
1、使用FileChannel(通道)和方法 read、write,完成文件的拷贝;
2、拷贝一个文本文件1.txt,放在项目下即可;
欢迎大家来到IT世界,在知识的湖畔探索吧!public class NIOFileChannel03 {
public static void main(String[] args) throws Exception{
FileInputStream fileInputStream = new FileInputStream("1.txt");
FileChannel fileChannel01 = fileInputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("2.txt");
FileChannel fileChannel02 = fileOutputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
//循环读取
while (true){
/**
* 里有一个重要的操作,一定不要忘记了
* public final Buffer clear() {
* position = 0;
* limit = capacity;
* mark = -1;
* return this;
* }
*/
//清空buffer,不然第一次读完后,因为limit=position,故read每次都是0
byteBuffer.clear();
int read = fileChannel01.read(byteBuffer);
//读取完了
if(read==-1){
break;
}
//将buffer中的数据写入到fileChannel02---2.txt
byteBuffer.flip();
fileChannel02.write(byteBuffer);
}
//关闭相关的流
fileInputStream.close();
fileOutputStream.close();
}
}
案例四:拷贝文件transferFrom方法
1、使用FileChannel(通道)和方法transferFrom,完成文件的拷贝
2、拷贝一张图片
public class NIOFileChannel04 {
public static void main(String[] args) throws Exception{
//创建相关流
FileInputStream fileInputStream = new FileInputStream("d:\\1.png");
FileOutputStream fileOutputStream = new FileOutputStream("d:\\11.png");
//获取各个流对应的fileChannel
FileChannel sourceChannel = fileInputStream.getChannel();
FileChannel destChannel = fileOutputStream.getChannel();
//使用transferFrom完成拷贝
destChannel.transferFrom(sourceChannel,0,sourceChannel.size());
//关闭相关通道和流
sourceChannel.close();
destChannel.close();
fileInputStream.close();
fileOutputStream.close();
}
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/34970.html