大多数人肯定了解Java IO, 但是对于NIO一般是陌生的,但是Java NIO是一个高频知识点,又不得不学,所以本文通过图文+代码的方式,保姆级别的讲述Java NIO的各个知识点。觉得写得好的,希望点个赞,给个。
Java IO 与 Java NIO读取文件的差别
普通Java IO
public static void ioReadFile(String fileName) throws Exception { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(fileName))); int len = 0; byte [] bytes = new byte[32]; while((len = bis.read(bytes)) != -1){ for(int i = 0; i < len; i++) { System.out.print((char) bytes[i]); } } bis.close(); }
Java NIO
public static void nioReadFile(String fileName) throws Exception{ RandomAccessFile read = new RandomAccessFile(fileName, "r"); FileChannel channel = read.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(32); while(channel.read(byteBuffer) > 0) { byteBuffer.flip(); while(byteBuffer.hasRemaining()){ System.out.print((char) byteBuffer.get()); } byteBuffer.clear(); } read.close(); channel.close(); }
Java IO与Java NIO主要有三个区别
普通IO是面向流(stream
)的处理,而NIO是面向缓冲区(buffer
)的处理
面向流
Java IO面向流意味着每次从流中读取一个或多个字节,直至读取所有字节,字节数据没有被缓存在任何地方,不能前后移动在流中读取数据的位置。
面向缓冲区
Java NIO面向缓冲区,数据读取到一个稍后处理的缓冲区,需要时可在缓冲区中前后移动。增加了处理过程中的灵活性。
普通IO是阻塞IO,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。而NIO是非阻塞IO,Java 一个线程从某通道发送请求读取数据,如果无可读取数据,其不会被阻塞,在数据变得可读取之前可以做其他事情
普通IO不支持selector,而NIO支持selector.(selector下文会详细解释).通过Selector对象,可以实现IO复用,下文会详细讲述。
从上面NIO读取文件的代码,可以看出,NIO方式读取文件,首先需要获取一个传输数据的通道
channel
,之后构建一个承载数据的buffer
.打个形象点的比喻就是channel
相当于铁路,而buffer
相当于货运火车,通过铁路,货运火车可以源源不断将货物,从出发点运抵目的地。其实还有一个selector
,构成NIO的三个核心。
channel的读写是双向的,既可以从通道读数据,又可以往通道写数据,而流一般是单向的,比如输入流,输出流,同时channel也支持异步得读写
channel主要有一下四种实现
buffer其实本质是一块可以写入数据,也可以读取数据的内存
其工作流程主要分为一下几个步骤
buffer主要有三个属性capacity
,position
,limit
,通过分析其三个属性,可以详细了解其工作原理
capacity
:buffer的大小,对于ByteBuffer,就是规定其能存储的最大byte数
buffer分为读模式和写模式,如下图所示
position
:下一个要被读或写的元素的位置。
写数据时,初始的position值为0.当一个byte、long等数据写到Buffer后,position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity-1.
读数据时,position也是从0开始,向前读取数据。写模式向读模式切换时,会将position重置为0
limit
:缓冲区里的数据总数。
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacity。当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据。
flip方法:从写模式切换到读模式,position置为0,limit设置为之前写模式的position值。
rewind方法:将position设置为0,但limit不变
clear方法:position设置为0,limit设置为capacity的值
compact方法:将所有未读的数据拷贝到Buffer起始处,然后将position设到最后一个未读元素正后面。limit属性依然像clear方法一样设置成capacity.现在Buffer准备好了写数据,但是不会覆盖未读数据。
mark、reset方法:通过调用Buffer.mark()方法,可以标记Buffer的一个特定position。之后可以通过调用Buffer.reset方法恢复到这个position
Buffer.mark(); //call Buffer.get() a couple of times, e.g. during parsing. Buffer.reset(); //set position back to mark.
Selector是channel的管理器,Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。这是在一个单线程中使用一个Selector处理3个Channel的图示:
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
java NIO复用,主要是通过Selector对象实现的,通过Selector对象,注册监听多个通道的多个事件,事件主要有四种,如下所示
OP_ACCEPT
: 监听连接事件,服务器监听客户端的连接请求
OP_CONNECT
:连接就绪事件,客户端与服务器已经连接成功
OP_READ
:读就绪事件,通道中已有可读数据,可以指向读操作
OP_WRITE
:写就绪事件,可以向通道中写数据
下面代码主要实现客户端向服务端传送图片数据的功能
client代码
public class NoBlockClient { public static void main(String[] args) throws IOException { //创建一个SocketChannel对象,通过从TCP读取数据,并绑定相应服务端ip和port SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 6666)); //设置为非阻塞IO socketChannel.configureBlocking(false); //为需要传送的图片文件建立channel FileChannel fileChannel = FileChannel.open(Paths.get("C:\Users\LENOVO\Desktop\1.png"), StandardOpenOption.READ); //创建运载数据所需的ByteBuffer对象 ByteBuffer buffer = ByteBuffer.allocate(1024); //从FileChannel中读取数据到ByteBuffer //将ByteBuffer对象写入SocketChannel while (fileChannel.read(buffer) != -1) { buffer.flip(); socketChannel.write(buffer); buffer.clear(); } //关闭相应通道 fileChannel.close(); socketChannel.close(); } }
Server代码
public class NoBlockServer { public static void main(String[] args) throws IOException { //创建一个ServerSocketChannel对象 ServerSocketChannel server = ServerSocketChannel.open(); //设置为非阻塞IO server.configureBlocking(false); //绑定监听端口 server.bind(new InetSocketAddress(6666)); //建立Selector对象,并注册OP_ACCEPT事件 Selector selector = Selector.open(); server.register(selector, SelectionKey.OP_ACCEPT); //selector.select()方法一直会阻塞,除非监听的事件就绪 while (selector.select() > 0) { Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); //有新的连接 if (selectionKey.isAcceptable()) { //为连接创建一个SocketChannel SocketChannel client = server.accept(); client.configureBlocking(false); //监听读事件 client.register(selector, SelectionKey.OP_READ); //可读 } else if (selectionKey.isReadable()) { SocketChannel client = (SocketChannel) selectionKey.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); //创建一个FileChannel,接受网络数据 FileChannel outChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); //从SocketChannel读取数据,并通过ByteBuffer,向FileChannel对象传输 while (client.read(buffer) > 0) { buffer.flip(); outChannel.write(buffer); buffer.clear(); } client.close(); } //处理完了的就绪事件,一定要删除,否则会反复处理 iterator.remove(); } } } }
} //处理完了的就绪事件,一定要删除,否则会反复处理 iterator.remove(); } } }
}
### 参考文章 [如何学习Java的NIO?](https://www.zhihu.com/question/29005375/answer/667616386)
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算