Java NIO之Java中的IO分类
前言
前面两篇文章(Java NIO之理解I/O模型(一)、Java NIO之理解I/O模型(二))介绍了,IO的机制,以及几种IO模型的内容,还有涉及到的设计模式。这次要写一些更贴近实际一些的内容了,终于要说到了Java中的各种IO了。我也是边学边理解,有写的不对的地方,欢迎小伙伴们指出和补充。
Java中的IO分类
BIO
BIO是指 Blocking IO 在JDK1.0的时候就引入了,直到JDK1.4一直都是Java中唯一的IO方式。它的主要实现方式就是,一个线程执行一个请求,如果请求数据量较大线程就会一直占用着,又或者请求什么也不做,也是会占用一个线程的,这样当客户端请求数量变多时,服务端线程数也跟着变多,最终就会导致服务端CPU崩溃的。当然可以使用线程池来减小CPU的压力,但是毕竟线程池也不是从本质上解决问题(长链接,线程池大小,以及拒绝策略等等因素都要考虑)。
无论是网络连接还是本地读取输出操作都是这种方式。具体的例子我就不举了(毕竟BIO不是本次的重点)。
NIO
Java中的NIO其实就是使用的多路I/O复用模型,前面的文章已经介绍过原理了,但是在理解Java的NIO之前,还是先介绍几个Java NIO的基础概念:Channel(通道),Buffer(缓冲区),Selector(选择器)。
Channel(通道)
Channel可以理解为,互通的管道,和Java的IO中的各种Stream(InputStream、OutputStream等等)一个等级,只不过Channel是双向的,而Stream是单向的。通道的作用是将数据移入或移出道各种I/O源,即可读又可写。
在Java中Channel类的层次结构相当复杂,有多个接口和许多可选操作。不过,常用的也就几个。
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
FileChannel可以对文件进行读和写,DatagramChannel可以以UDP的协议来进行数据读写,SocketChannel以TCP的协议来对网络两端进行读写,ServerSocketChanel能够监听客户端发起的TCP连接,并为每个TCP连接创建一个新的SocketChannel来进行数据读写。
Buffer(缓冲区)
Buffer是一个高效的数据容器,在NIO中所有的数据操作都必须经过缓冲区,这点是和BIO不同的,BIO是直接将数据写到Stream对象中的。因为Stream对象的设计是按顺序一个字节一个字节的传送数据。虽然出于性能考虑,也可以传递字节数组,但是基本概念都是一个字节一个字节的传递数据。通道与之不同之处在于,通道会传送缓冲区的数据块,而且通道的基本概念就是按照一个数据块一个数据块的去读和写。所以也可以将缓冲区理解为一个字节数组,专门用来存储以及准备好出入通道的字节。
如下图:
如上图所示,无论是客户端发送和接收数据,还是服务端接收和相应数据,都是从缓冲区中进行数据操作的。
在Java中除了boolean外,所有的基本数据类型都有特定的Buffer子类:
ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。网络程序几乎只会使用ByteBuffer,但程序偶尔也会使用其他类型来取代ByteBuffer。
除了数据列表外,每个缓冲区都记录了信息的4个关键部分。无论是何种类型,都有相同的方法来获取和设置这些值:
位置(position)
缓冲区中将读取或写入的下一个位置。这个位置从0开始计,最大值等于缓冲区的大小。可以用下面两个方法获取和设置。
public final int position();
public final Buffer position(int newPosition);
容量(capacity)
缓冲区可以保存的最大数目。容量值在创建缓冲区时设置,此后不能改变。
可以用以下方法读取:
public final int capacity();
限度(limit)
缓冲区中可访问数据的末尾位置。只要不改变限度,就无法读/写超过这个位置的数据,即使缓冲区有更大的容量也没有用。限度可以用下面两个放获取和设置。
public final int limit();
public final Buffer limit(int newLimit);
标记(mark)
缓冲区中客户端指定的索引。通过调用mark()可以将标记设置为当前位置。调用reset()可以将当前位置设置为所标识的位置。
public final Buffer mark() ;
public final Buffer reset();
如果将位置设置为低于现有标记,则丢弃这个标记。
与读取InputStream不同,读取缓冲区实际上不会以任何方式改变缓冲区中的数据。只可能向前或向后设置位置,从而可以从缓冲区中某个特定位置开始读取。类似的程序可以调整限度,从而控制将要读取的数据的末尾。只有容量是固定的。
Selector(选择器)
Selector是Java NIO中最重要的一部分,Selector的作用就是用单线程来轮询处理注册的Channel,一旦哪个Channel的数据准备就绪了,就可以进行处理了。
如下图:
使用Selector,要先向Selector中注册Channel, 然后调用它的select()方法,这个方法会一直阻塞到某个注册的Channel中的事件准备就绪。一旦select()方法返回,线程就可以处理这些事件了,比如新的连接进入,数据接收等。
Selector类并没有注册新通道的方法,register()方法是在SelectableChannel类中声明。SelectableChannel类是实现自Channel接口的,它支持将Channel注册到Selector中。
SelectableChannel提供了两个注册Channel的方法:
public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;
public final SelectionKey register(Selector sel, int ops) throws ClosedChannelException;
第一个参数代表通道要向哪个选择器注册。第二个参数是SelectionKey类中的一个命名常量,标识所注册的操作。
SelectionKey定义了4个命名位常量,用户选择操作类型:
SelectionKey.OP_READ;
SelectionKey.OP_WRITE;
SelectionKey.OP_CONNECT;
SelectionKey.OP_ACCEPT;
当一个通道需要在同一个选择器中关注多个操作,只需要用户位“或”操作符组合这些常量即可。
channel.register(selector,SelectionKey.OP_READ | SelectionKey.OP_WRITE);
第三个参数是可选的,代表键的附件。这个参数通常用户存储链接状态,例如:如果要实现一个Web服务器,可能要附加一个FileInputStream或FileChannel,这个流或通道连接到服务器提供给客户端的本地文件。
例子:
复制文件的读写操作,可以用来举例说明NIO的一个大致过程。
public static void copyFileByNIO(String src,String dst) throws IOException { //声明源文件和目标文件
RandomAccessFile aFile = new RandomAccessFile(src, "rw");
RandomAccessFile bFile = new RandomAccessFile(dst, "rw"); //获得传输通道channel
FileChannel inChannel = aFile.getChannel();
FileChannel outChannel = bFile.getChannel();
//获得容器buffer
ByteBuffer buffer= ByteBuffer.allocate(1024);
while(true){
//判断是否读完文件
int eof =inChannel.read(buffer);
if(eof==-1){
break;
}
//重设一下buffer的position=0,limit=position
buffer.flip();
//开始写
outChannel.write(buffer);
//写完要重置buffer,重设position=0,limit=capacity
buffer.clear();
}
inChannel.close();
outChannel.close();
aFile.close();
bFile.close();
}
AIO
AIO这次就不介绍了,我后续要单独的拿出一整篇来介绍AIO。
Java NIO之Java中的IO分类的更多相关文章
- 【Java nio】java nio笔记
缓冲区操作:缓冲区,以及缓冲区如何工作,是所有I/O的基础.所谓“输入/输出”讲的无非就是把数据移出货移进缓冲区.进程执行I/O操作,归纳起来也就是向操作系统发出请求,让它要么把缓冲区里的数据排干,要 ...
- Java-杂项-java.nio:java.nio
ylbtech-Java-杂项-java.nio:java.nio java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有 ...
- 【JAVA NIO】java NIO
本文是博主深入学习Netty前的一些铺垫,之前只是使用Netty,用的很粗暴,导包,上网找个DEMO就直接用,对Netty中的组件了解并不深入. 于是再此总结下基础,并对一些核心组件作如下记录: 1. ...
- 我的Java开发学习之旅------>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常
今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了java.nio.charset.MalformedInputException: Input length = 1异常, ...
- JAVA NIO Scatter/Gather(矢量IO)
矢量IO=Scatter/Gather: 在多个缓冲区上实现一个简单的IO操作.减少或避免了缓冲区拷贝和系统调用(IO) write:Gather 数据从几个缓冲区顺序抽取并沿着通道发送,就好 ...
- Java中的IO与NIO
前文开了高并发学习的头,文末说了将会选择NIO.RPC相关资料做进一步学习,所以本文开始学习NIO知识. IO知识回顾 在学习NIO前,有必要先回顾一下IO的一些知识. IO中的流 Java程序通过流 ...
- Java NIO中核心组成和IO区别
1.Java NIO核心组件 Java NIO中有很多类和组件,包括Channel,Buffer 和 Selector 构成了核心的API.其它组件如Pipe和FileLock是与三个核心组件共同使用 ...
- java nio(non-blocking io)简介及和io
在 Java1.4之前的I/O系统中,提供的都是面向流的I/O系统,系统一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节 的数据,面向流的I/O速度非常慢,而在Java 1 ...
- JAVA NIO中的Channels和Buffers
前言 Channels和Buffers是JAVA NIO里面比较重要的两个概念,NIO正是基于Channels和Buffers进行数据操作,且数据总是从Channels读取到Buffers,或者从Bu ...
随机推荐
- php tp5 composer
## php tp5 composer安装tp5.1需要先去装个Apache或者Nginx,再装个php环境.一般Windows可以直接使用xmapp.然后tp5好像python的django啊... ...
- JavaScript之对象基础
现实世界是由无数的事物或对象组成,事物都有各独特的属性和一些动作行为,一般我们可以通过事物的某些特征或行为动作描述它.JavaScript中也是如此,JavaScript对象是多个键值对的集合,键我们 ...
- rocketmq学习(二) rocketmq集群部署与图形化控制台安装
1.rocketmq图形化控制台安装 虽然rocketmq为用户提供了使用命令行管理主题.消费组以及broker配置的功能,但对于不够熟练的非运维人员来说,命令行的管理界面还是较难使用的.为此,我们可 ...
- 首次接触flask遇到socket.error: [Errno 10013] 报错
解决方案: 发现是因为端口5000被占用了,设置一个其他port就行:app.run(port=5050)
- JDBC对Mysql utf8mb4字符集的处理
写在前面 在开发微信小程序的时候,评论服务模块希望添加上emoji表情,但是emoji表情是4个字节长度的,所以需要进行设置 当前项目是JAVA编写, 使用JDBC连接操作数据库, 如下针对的JDBC ...
- Hbase入门(三)——数据模型
Hbase最核心但也是最难理解的就是数据模型,由于与传统的关系型数据库不同,虽然Hbase也有表(Table),也有行(Row)和列(Column),但是与关系型数据库不同的是Hbase有一个列族(C ...
- Docker系列(五):.Net Core实现k8s健康探测机制
k8s通过liveness来探测微服务的存活性,判断什么时候该重启容器实现自愈.比如访问 Web 服务器时显示 500 内部错误,可能是系统超载,也可能是资源死锁,此时 httpd 进程并没有异常退出 ...
- POJ 3069——Saruman's Army(贪心)
链接:http://poj.org/problem?id=3069 题解 #include<iostream> #include<algorithm> using namesp ...
- Transactional事务提交后触发异步方法
一.问题复现 1.场景 2个service方法, 方法A中调用方法B. 方法A 是核心业务方法,涉及多张表数据变更,为了保持数据一致,用spring事务注解:@Transactional(rollba ...
- Java 学习笔记之 线程Priority
线程Priority: 线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务. 设置线程优先级有助于帮助“线程规划器”确定在下一次选择哪个线程来优 ...