图解Java NIO

目录:

NIO结构

NIO与传统IO异同

NIO使用步骤

NIO代码

ByteBuffer难点解析


1:NIO结构:

Channel:通道,连接客户端和服务端的一个管道,管道内可以双向传输数据。

Selector:选择器,可以想象成一个环状传送带,上面可以接入很多管道,selector还可以对每个管道设置感兴趣的颜色(连接(红色),读(黄色),写(蓝色),接收数据)。当Selector开始轮询的时候Selector这个传送带就一直转动,当某个管道被传送到感兴趣事件检查点的时候,selector会检查改管道当前颜色(即事件)之前是否被注册成了感兴趣颜色(事件),如果感兴趣,那么Selector就可以对这个管道做处理了,比如把管道传给别的线程,让别的线程完成读写操作。

ByteBuffer:字节缓冲区,本质上是一个连续的字节数组,Selector感兴趣的事件发生后对管道的读操作所读到的数据都存储在ByteBuffer中,而对管道的写操作也是以ByteBuffer为源头,值得注意的是ByteBuffer可以有多个。想想看,我们使用的所有基本类型都可以转换成byte字节,比如Integer类型占4字节,那么传递数字 1 ByteBuffer底层数组被占用了4个格子。


2:与传统IO比较:

1:传统IO一般是一个线程等待连接,连接过来之后分配给processor线程,processor线程与通道连接后如果通道没有数据过来就会阻塞(线程被动挂起)不能做别的事情。NIO则不同,首先:在Selector线程轮询的过程中就已经过滤掉了不感兴趣的事件,其次:在processor处理感兴趣事件的read和write都是非阻塞操作即直接返回的,线程没有被挂起。

2:传统IO的管道是单向的,NIO的管道是双向的

3:两者都是同步的,也就是Java程序亲力亲为的去读写数据,不管传统IO还是NIo都需要read和write方法,这些都是Java程序调用的而不是系统帮我们调用的。NIO2.0里这点得到了改观,即使用异步非阻塞AsynchronousXXX四个类来处理。

3:使用NIO步骤:(服务端)

首先:创建一个传送带

然后:创建一个管道,设置管道为非阻塞,绑定端口

然后:把管道放到传送带上

再然后:启动传送带

其次:传送带感兴趣事件检查点查获一个感兴趣管道,转给其他线程对管道进行非阻塞读写

最后:全使用完,关闭管道

过程很清晰,跟我们现实世界中的传送带效果一样。


4:代码体现:

注意:只写服务器端关键步骤,客户端可以参考这些代码

public class Server implements Runnable {

private Selector selector;

private ByteBuffer buffer = ByteBuffer.allocate(1024);

public Server(int port) {

        try {

            //1 创建一个传送带

            selector = Selector.open();

            //2 创建一个管道

            ServerSocketChannel ssc = ServerSocketChannel.open();

            //3 设置服务器通道为非阻塞方式

            ssc.configureBlocking(false);

            //4 绑定TCP地址

            ssc.bind(new InetSocketAddress(port));

            //5 把管道放到传送带上,并在传送带上注册一个感兴趣事件,此处传送带感兴趣事件为连接事件

            ssc.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("Server start, port:" + port);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

public void run() { 

 while (true) {

    try {

                //1 启动传送带,开始轮询

                selector.select();

                //2 所有感兴趣事件的keys

               SelectionKey Iterator keys = selector.selectedKeys().iterator();

                //3 遍历所有感兴趣事件集合

                while (keys.hasNext()) {

                    SelectionKey key = keys.next();

                    keys.remove();

                    if(key.isValid()) { //如果key的状态是有效的

                        if(key.isAcceptable()) { //如果key是阻塞状态,则调用accept()方法

                            accept(key);

                        }

                        if(key.isReadable()) { //如果key是可读状态,则调用read()方法

                            read(key);

                        }

                    }

                }

            } catch (IOException e) {

                e.printStackTrace();

            }

        }

    }

    private void accept(SelectionKey key) {

        try {

            //1 获取服务器通道

            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();

            //2 执行阻塞方法

            SocketChannel sc = ssc.accept();

            //3 设置阻塞模式为非阻塞

            sc.configureBlocking(false);

            //4 注册到多路复用选择器上,并设置读取标识

            sc.register(selector, SelectionKey.OP_READ);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    private void read(SelectionKey key) {

        try {

            //1 清空缓冲区中的旧数据

            buffer.clear();

            //2 获取之前注册的SocketChannel通道

            SocketChannel sc = (SocketChannel) key.channel();

            //3 将sc中的数据放入buffer中

            int count = sc.read(buffer);

            if(count == -1) { // == -1表示通道中没有数据

                key.channel().close();

                key.cancel();

                return;

            }

            //读取到了数据,将buffer的position复位到0

            buffer.flip();

            byte[] bytes = new byte[buffer.remaining()];

            //将buffer中的数据写入byte[]中

            buffer.get(bytes);

            String body = new String(bytes).trim();

            System.out.println("Server:" + body);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    public static void main(String[] args) {

        new Thread(new Server(8379)).start();

    }

}

其他:

涉及到ByteBuffer分类及ByteBuffer的读写这里就不过多介绍了,就是一些指针和模式的变动,主要是flip方法,调用flip方法之后的变化从写模式变成读模式


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容

  • 前言: 之前的文章《Java文件IO常用归纳》主要写了Java 标准IO要注意的细节和技巧,由于网上各种学习途径,...
    androidjp阅读 2,900评论 0 22
  • Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java I...
    JackChen1024阅读 7,554评论 1 143
  • 转自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的猫阅读 2,299评论 0 22
  • [TOC] > 从JDK NIO文档可以看到,Java将其划分成了三大块:Channel,Buffer以及Sele...
    Ever_00阅读 1,096评论 0 3
  • 文/洛羽 满溺出一杯想念的思愁 那是风对雨的思念 在午夜的灯光下 我满上了一杯酒 嗅着空气中弥漫的芬芳啊 静静的,...
    风洛天羽阅读 743评论 0 5