Java nio都是非阻塞IO么?并非如此

java中的nio包,对于java程序员来说是个熟悉又陌生的东西。以前一直以为nio=Non-blocking I/O,即非阻塞IO。后来又听人说nio其实是new IO新一代IO的意思。两种说法到底哪种是正确的?我去Oracle的java官网查看doc,很遗憾也没直接解释nio是什么单词的缩写。但是经过一番实践,确证new IO才是nio正确的全称。

一段代码引发的思考

    ByteBuffer buf = ByteBuffer.allocate(48);
    buf.clear();

    channelA.read(buf);

    buf.flip();

    while(buf.hasRemaining()) {
        channelB.write(buf);
    }

这是在网上搜FileChannel看到的一段代码。这个时候我以为nio下的一切io操作都是非阻塞的,于是看到这段代码我就非常费解。
为什么FileChannel在read的时候没有加while判断,在write的时候就要加while判断了?难道说对FileChannel来说read是线程阻塞操作,write是非阻塞的?这就有点匪夷所思了。看到doc文档也没找到答案。

于是我自己试验了一下,在while循环里添加日志,如果write是非阻塞的,那么while应该打印很多次。最后的结果是while里只打印一次日志,代表read和write都是阻塞的!
这时候其实我更迷惑了,因为我一直以为nio下的io操作都是非阻塞的。
于是再次去百度FileChannel到底是阻塞还是非阻塞的。找了很久没找到确切的答案,最后在StackOverflow上找到了一个比较满意的回答:

UNIX does not support non-blocking I/O for files, see Non-blocking I/O with regular files. As Java should (at least try to) provide the same behaviour on all platforms, the FileChannel does not implement SelectableChannel.
UNIX不支持文件的非阻塞IO,参看这个网页的说明。由于Java在全平台上要保持行为一致(或者努力这么做),所以FileChannel没有实现SelectableChannel。

However Java 7 will include a new AsynchronousFileChannel class that supports asynchronous file I/O, which is a different mechanism to non-blocking I/O.
但是Java7上引入了一个支持异步文件IO的AsynchronousFileChannel类,但是这是跟非阻塞IO不一样的机制。

In general only sockets and pipes truly support non-blocking I/O via select() mechanism.
一般来说只有socktes和pipes通过select机制真正支持非阻塞IO。

从这段话来说,首先FileChannel肯定是阻塞IO的;其次实现了SelectableChannel接口才能实现非阻塞IO。从FileChannel上也能看出来,nio下不是所有io操作都是非阻塞的。因此nio的全称绝不应该是non-blocking I/O,而应该是new IO。
当然这里还说了异步和非阻塞是完全不一样的机制,这个以后再来了解吧。

Channels&Buffers

nio下有很多类,对我们来说以下三个是最重要的:

  • Channels
  • Buffers
  • Selectors
    第一代io使用字节流和字符流来进行读写,而nio使用Channels和Buffers来实现读写。数据总是从Channel读到buffer里,再从buffer写到Channel里。这个是使用上很大的区别。
    Channel和字符字节流有点类似,但是Channel都是双向的,既可以读,也可以写。
    以前的io流我们一般直接定义一个byte数组来作为buffer,但是nio里需要使用Buffer类。
    下面是网上找的一个使用FileChannel和Buffer的例子:
    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
    FileChannel inChannel = aFile.getChannel();

    //创建48个字节长度的ByteBuffer
    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = inChannel.read(buf); //从Channel中读取数据到buffer里
    while (bytesRead != -1) {

    buf.flip();  //切换buffer到读取状态

    while(buf.hasRemaining()){
        System.out.print((char) buf.get()); //每次读取一个字节
    }

    buf.clear(); //清空buffer后才能继续从Channel里读取数据
    bytesRead = inChannel.read(buf);
    
    aFile.close();

Buffer实例化的时候需要传入长度。在buffer写好数据,准备读取到Channel里的时候,一定要执行flip操作,buffer才可以读取。同样的,再次写入数据之前,buffer需要clear一下清除数据。

Selector和非阻塞IO

但是nio最吸引我们的肯定还是非阻塞IO,而java nio里非阻塞IO的实现要靠Selector。Selector是一种IO多路复用机制的实现。简单来说,就是用单个线程/进程来对多个IO Channel进行轮询。内核不管IO有没有完成都会立即返回给用户,这样单个IO的阻塞就不会阻塞用户线程。单个线程无需一个个等待IO完成再工作,而是发现有完成的就处理,处理完继续轮询,直到下一个完成的IO出现。

这样做的好处就是单个线程即可处理海量并发请求,当然前提是业务的数据处理不能太耗时,否则线程会卡在数据处理上。NodeJs就是单线程非阻塞IO模型,因此擅长处理高并发。同样适合非阻塞IO的还有nginx、redis等高并发但是计算简单的软件。而Java数据库IO连接的JDBC是阻塞io,因此Java服务器不太适合nio,而适合多线程来处理并发。

对Android程序员来说,Looper中的MessgeQueue在执行next方法获取下一条handler消息的时候,如果没有消息,主线程执行nativePollOnce方法阻塞住。这样主线程在没事的时候就不会消耗cpu资源。在有消息传过来时,android framework除了把消息添加到MessageQueue,还会把主线程给wakeup。那么怎么样block(阻塞)线程和wakeup(唤醒)线程呢?通过linux系统的epoll机制,而epoll机制就是selector相对的poll机制的加强版。

以上只是我的一点形而上的理解,有可能存在谬误,推荐几个解释的好的博文来学习Selector以及IO多路复用的概念:

Java NIO 系列教程(翻译的国外教程)

聊聊Linux 五种IO模型

StackOverflow上关于NativePollOnce方法的讨论

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

推荐阅读更多精彩内容

  • 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 https://g...
    da3acf50377b阅读 390评论 0 1
  • 这两天了解了一下关于NIO方面的知识,网上关于这一块的介绍只是介绍了一下基本用法,没有系统的解释NIO与阻塞、非阻...
    Ruheng阅读 7,122评论 5 48
  • 同步和异步、阻塞和非阻塞 同步 (synchronous) 是一种可靠的运行机制,当我们进行同步操作时,后续操作是...
    wean_a23e阅读 206评论 0 0
  • IO流学习总结 一Java IO,硬骨头也能变软 (1) 按操作方式分类结构图: (2)按操作对象分类结构图 二j...
    绿叶悠阅读 618评论 0 0
  • (1) 按操作方式分类结构图: (2)按操作对象分类结构图 二java IO体系的学习总结 IO流的分类:按照流的...
    JavaEdge阅读 538评论 0 13