博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java NIO
阅读量:5883 次
发布时间:2019-06-19

本文共 3565 字,大约阅读时间需要 11 分钟。

  hot3.png

概述

这段时间看了不少java NIO的教程,网上找到的基本都是大同小异。源头都出自一位老外的技术分享:NIO教程。基于这些知识点结合自己的理解在这里阐述一下。

三大核心组件

NIO中由三大核心组件,分别是Channel、Buffer、Selector。

Channel和Buffer

Channel和Buffer就类似于java IO中的Stream,NIO通过Channel读取数据到Buffer中或者从Buffer中写入数据到Channel,而IO中的Stream要么只能读取要么只能写入。Channel的读写可以设置成非阻塞。Channel和Buffer相比于Stream的不同点:

  • 同一个Buffer有写入模式和读取模式(flip方法切换),同一个Stream只读或者只写
  • Channel可以设置成非阻塞,Stream都是阻塞的

Buffer

NIO中的Buffer,可以理解为缓冲区,Channel中的数据必须通过Buffer流转。我们可以往Buffer中写数据,也可以从Buffer中读数据。

Buffer中定义了4个属性:mark、position、limit、capacity,并且保证mark <= position <= limit <= capacity。每个属性代表不同的意思:

  • position,用于保存读写的位置,初始为0,写一个数据或者读一个数据,position就往后移动一个位置,position++,调用flip方法position=0;
  • limit,是可写或者可读的限制,初始为capacity,调用flip方法会将position赋值给limit,limit=position;
  • capacity,容量初始设置就不变了
  • mark,用来标记用,调用mark方法设置标记mark=position,调用reset方法将position重置到上次设置的位置position=mark;

由于Buffer的设计限制,Buffer只能要么写要么读,要正确的将数据从Buffer中读取出来,记得必须调用flip方法。

  1. Buffer写数据
  2. 调用flip方法
  3. Buffer读取数据
  4. 调用clear方法

Buffer中几个方法都是被final修饰,不能被子类重写

flip

public final Buffer flip() {    limit = position;    position = 0;    mark = -1;    return this;}

clear

清空数据只是几个属性被重置成初始状态

public final Buffer clear() {    position = 0;    limit = capacity;    mark = -1;    return this;}

rewind

rewind方法重置position,表示重写,或者重读

public final Buffer rewind() {    position = 0;    mark = -1;    return this;}

Selector

Selector对象用来监控多个Channel的状态,一次select能够将已经准备就绪的Channel挑选出来,然后可以对这些Channel进行相应的IO操作。这样就用一个Selector对象就能管理多个Channel连接了。

传统的Socket中,一个ServerSocket对象每次accept一个Socket连接,然后使用这个Socket的InputStream和OutputStream分别进行读写,直到这个Socket关闭之前这都是阻塞的。特别处理长连接的情况下,在这个Socket关闭之前,可能大部分时间内都可能没有数据进行传输,但是还是必须阻塞着。为了避免被一个Socket阻塞而影响下一个Socket的accept,一般的做法都是用一个Thread处理一个Socket,这样的做法虽然解决了阻塞的问题,但是衍生出另一个问题,就是会随着Socket的增加而产生很多的Thread,而且大部分时间内这些Thread都是没什么事情可做,非常浪费系统资源。

为了解决传统Socket中的问题,NIO定义了Selector。一个Selector能够监控多个Channel的状态,不管是ServerSocketChannel还是SocketChannel,都可以注册到一个Selector上。Selector在一次select后,会把已经准备就绪的Channel挑选出来,然后可以对这些Channel进行相应的IO操作。每个Channel的就绪状态分别为read、write、connect、accept,根据不同状态处理不同的方法,处理完了之后,下一次如果又准备就绪了还会被Selector再一次select出来,这样每个Channel都能被Selector多次复用。而且就算是长连接每次也只处理一种事件类型,这样也不会造成阻塞,只要一个线程就够了。这种模型就能作用只使用一个线程就可以操作大量的客户端连接。

Selector和Reactor模式和IO多路复用,这三者到底是什么关系?

register方法

需要使用Selector监控,必须首先注册,通过调用SelectableChannel.register()方法。Channel调用这个方法,使用必须配置为非阻塞的,并且注册的时候需要指定这个Channel所关注的事件,分为read、write、connect、accept。当这个Channel所关注的事件准备就绪之后,Selector才会通过select方法返回。比如,一个Channel的read事件就绪了,但是在注册到Selector的时候没有关注read类型,那么Selector也不会select出来,可以查看Channel的translateReadyOps方法。

SelectionKey

调用register方法返回SelectionKey对象,对象关联了Channel、Selector、还包括Channel所关注的事件(interestOps),和这个Channel准备就绪的事件(readyOps)。

select方法

Selector对象通过一次select方法,返回准备就绪满足条件的Channel数量,如果>0,后续调用selectedKeys方法返回SelectionKey的set集合。通过迭代set,对不同的事件进行不同的处理。

select方法是一个阻塞方法,知道有Channel就绪了才会返回,也可以被wakeup方法唤醒。select方法有一个重载方法,select(timeout),超时返回。selectNow方法非阻塞,都没有可用就绪的Channel就返回0。Selector的select方法,每次返回这个调用以来的准备就绪的Channel,比如第一次select有一个就绪,返回1,再一次调用又有一个就绪,还是返回1

Set
selectedKeys = selector.selectedKeys();Iterator
keyIterator = selectedKeys.iterator();while(keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if(key.isAcceptable()) { // a connection was accepted by a ServerSocketChannel. } else if (key.isConnectable()) { // a connection was established with a remote server. } else if (key.isReadable()) { // a channel is ready for reading } else if (key.isWritable()) { // a channel is ready for writing } // 必须remove掉,因为SelectionKey不会自己remove,我们处理完一个Channel必须remove掉 keyIterator.remove();}

 

转载于:https://my.oschina.net/cregu/blog/2252688

你可能感兴趣的文章
Zeppelin的入门使用系列之使用Zeppelin运行shell命令(二)
查看>>
form表单下的button按钮会自动提交表单的问题
查看>>
那些年追过的......写过的技术博客
查看>>
python基础教程_学习笔记19:标准库:一些最爱——集合、堆和双端队列
查看>>
CSS魔法堂:Transition就这么好玩
查看>>
解决win7远程桌面连接时发生身份验证错误的方法
查看>>
C/C++ 多线程机制
查看>>
python mysql Connect Pool mysql连接池 (201
查看>>
Boost在vs2010下的配置
查看>>
一起谈.NET技术,ASP.NET伪静态的实现及伪静态的意义
查看>>
20款绝佳的HTML5应用程序示例
查看>>
string::c_str()、string::c_data()及string与char *的正确转换
查看>>
11G数据的hive初测试
查看>>
如何使用Core Text计算一段文本绘制在屏幕上之后的高度
查看>>
==和equals区别
查看>>
2010技术应用计划
查看>>
XML 节点类型
查看>>
驯服 Tiger: 并发集合 超越 Map、Collection、List 和 Set
查看>>
Winform开发框架之权限管理系统改进的经验总结(3)-系统登录黑白名单的实现...
查看>>
LeetCode – LRU Cache (Java)
查看>>