BIO:

  BIO是阻塞IO,体现在一个线程调用IO的时候,会挂起等待,然后Thread会进入blocked状态;这样线程资源就会被闲置,造成资源浪费,通常一个系统线程数是有限的,而且,Thread进入内核态也是很大的性能开销。而阻塞方式,意味着BIO必然是一个同步IO。

  BIO还有一个显著的特点是面向流式Stream编程,特点是实现简单,但也意味着拓展性差。

NIO:

  NIO,通常实现为同步非阻塞IO,同步意味着不会产生会调,需要线程自身去同步IO是否完成,而非阻塞就是线程会立刻返回。

  相对于BIO面向流式抽象思想编程,NIO是面向管道编程的,例如在Java中必谈的三个封装类Buffer、Channel、Sellector,就是管道编程的体现,Java1.4后提供的非阻塞 IO 的核心在于使用一个 Selector 来管理多个通道,可以是 SocketChannel,也可以是 ServerSocketChannel,将各个通道注册到 Selector 上,指定监听的事件。之后可以只用一个线程来轮询这个 Selector,看看上面是否有通道是准备好的,当通道准备好可读或可写,然后才去开始真正的读写,这样速度就很快了。我们就完全没有必要给每个通道都起一个线程。如下面代码所示:

  1. package com.mobisummer.spider.slave.task.aliexpress.region;
  2.  
  3. import java.io.IOException;
  4. import java.net.InetSocketAddress;
  5. import java.net.ServerSocket;
  6. import java.nio.ByteBuffer;
  7. import java.nio.channels.SelectionKey;
  8. import java.nio.channels.Selector;
  9. import java.nio.channels.ServerSocketChannel;
  10. import java.nio.channels.SocketChannel;
  11. import java.util.Iterator;
  12. import java.util.Set;
  13.  
  14. public class PlainNioServer {
  15.  
  16. public void serve(int port) throws IOException {
  17.  
  18. ServerSocketChannel serverChannel = ServerSocketChannel.open();
  19. serverChannel.configureBlocking(false);
  20. ServerSocket ssocket = serverChannel.socket();
  21. InetSocketAddress address = new InetSocketAddress(port);
  22. ssocket.bind(address);
  23. Selector selector = Selector.open();
  24. serverChannel.register(selector, SelectionKey.OP_ACCEPT);
  25. final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());
  26.  
  27. for (; ; ) {
  28. try {
  29. selector.select();
  30. } catch (IOException ex) {
  31. ex.printStackTrace();
  32. // handle exception
  33. break;
  34. }
  35. Set<SelectionKey> readyKeys = selector.selectedKeys();
  36. Iterator<SelectionKey> iterator = readyKeys.iterator();
  37. while (iterator.hasNext()) {
  38. SelectionKey key = iterator.next();
  39. iterator.remove();
  40. try {
  41. if (key.isAcceptable()) {
  42. ServerSocketChannel server = (ServerSocketChannel) key.channel();
  43. SocketChannel client = server.accept();
  44. client.configureBlocking(false);
  45. client.register(selector,
  46. SelectionKey.OP_WRITE | SelectionKey.OP_READ,
  47. msg.duplicate());
  48. System.out.println("Accepted connection from " + client);
  49. }
  50. if (key.isWritable()) {
  51. SocketChannel client = (SocketChannel) key.channel();
  52. ByteBuffer buffer = (ByteBuffer) key.attachment();
  53. while (buffer.hasRemaining()) {
  54. if (client.write(buffer) == 0) {
  55. break;
  56. }
  57. }
  58. client.close();
  59. }
  60. } catch (IOException ex) {
  61. key.cancel();
  62. try {
  63. key.channel().close();
  64. } catch (IOException cex) {
  65. // ignore on close
  66. }
  67. }
  68. }
  69. }
  70. }
  71. }

  对于并发数量大但处理的任务又十分快速的时候用处十分显著,代替了之前的利用多线程解决业务问题的方案,就是利用单线程以及底层epoll或者poll原理完成了单线程处理多任务的方案,理论上至少我们想到了减少线程切换的开支,而由内核去改变IO状态。

  【说说实现】

  NIO 中 Selector 是对底层操作系统实现的一个抽象,管理通道状态其实都是底层系统实现的,在不同系统下的实现会不同,是自动选择的,可能的实现方式如下:

  select:上世纪 80 年代的事情了,它支持注册 FD_SETSIZE(1024) 个 socket,在那个年代肯定是够用的。

  poll:1997 年,出现了 poll 作为 select 的替代者,最大的区别就是,poll 不再限制 socket 数量。

  select 和 poll 都有一个共同的问题,那就是它们都只会告诉你有几个通道准备好了,但是不会告诉你具体是哪几个通道。所以,一旦知道有通道准备好以后,自己还是需要进行一次扫描,显然这个不太好,通道少的时候还行,一旦通道的数量是几十万个以上的时候,扫描一次的时间都很可观了,时间复杂度 O(n)。所以,后来才催生了以下实现。

  epoll:2002 年随 Linux 内核 2.5.44 发布,epoll 能直接返回具体的准备好的通道,时间复杂度 O(1)。那么这个epoll是怎么的原理呢?这就涉及操作系统的中断了,在内核的最底层是中断,类似系统回调的机制。网卡设备对应一个中断号, 当网卡收到网络端的消息的时候会向CPU发起中断请求, 然后CPU处理该请求. 通过驱动程序 进而操作系统得到通知, 系统然后通知epoll, epoll改变阻塞状态。

  除了 Linux 中的 epoll,2000 年 FreeBSD 出现了 Kqueue,还有就是,Solaris 中有 /dev/poll。

  前面说了那么多实现,但是没有出现 Windows,Windows 平台的非阻塞 IO 使用 select,我们也不必觉得 Windows 很落后,在 Windows 中 IOCP 提供的异步 IO 是比较强大的。

AIO:

  异步这个词,我想对于绝大多数开发者来说都很熟悉,很多场景下我们都会使用异步。对于我而言比较有意义的事情就是发现我所在公司自己做的底层框架Lwmf,自己做了一个声称为AIO的实现,只不过是封装了一层罢。

  通常,我们会有一个线程池用于执行异步任务,提交任务的线程将任务提交到线程池就可以立马返回,不必等到任务真正完成。如果想要知道任务的执行结果,通常是通过传递一个回调函数的方式,任务结束后去调用这个函数。

  同样的原理,Java 中的异步 IO 也是一样的,都是由一个线程池来负责执行任务,然后使用回调或自己去查询结果,所以这里涉及了两个实现方式,在Java中就是注册回调函数和使用异步任务返回的Feature实例。

  干货在这里:对象是过程的抽象,而线程是调度的抽象;所以,设计异步IO的时候,需要把线程控制的牢牢的,才能更稳健的设计哦。

  最后,不得不提一下的就是Reactor模型和Netty框架了!但不是本文重点,但这确实是java中优秀的NIO实现

【谈谈IO】BIO、NIO和AIO的更多相关文章

  1. Java提供了哪些IO方式?IO, BIO, NIO, AIO是什么?

    IO一直是软件开发中的核心部分之一,而随着互联网技术的提高,IO的重要性也越来越重.纵观开发界,能够巧妙运用IO,不但对于公司,而且对于开发人员都非常的重要.Java的IO机制也是一直在不断的完善,以 ...

  2. 理解IO、NIO、 AIO

    转载:https://baijiahao.baidu.com/s?id=1586112410163034993&wfr=spider&for=pc nio 同步: 自己亲自出马持银行卡 ...

  3. Java BIO NIO 与 AIO

    回顾 上一章我们介绍了操作系统层面的 IO 模型. 阻塞 IO 模型. 非阻塞 IO 模型. IO 复用模型. 信号驱动 IO 模型(用的不多,知道个概念就行). 异步 IO 模型. 并且介绍了 IO ...

  4. BIO,NIO与AIO的区别

    Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理.Java AIO(NIO.2 ...

  5. Java的BIO,NIO和AIO的区别于演进

    作者:公众号:我是攻城师 前言 Java里面的IO模型种类较多,主要包括BIO,NIO和AIO,每个IO模型都有不一样的地方,那么这些IO模型是如何演变呢,底层的原理又是怎样的呢? 本文我们就来聊聊. ...

  6. java IO、NIO、AIO详解

    概述 在我们学习Java的IO流之前,我们都要了解几个关键词 同步与异步(synchronous/asynchronous):同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调 ...

  7. Socket网络通信——IO、NIO、AIO介绍以及区别

    一 基本概念 Socket又称"套接字",应用程序通常通过"套接字"向网路发出请求或者应答网络请求. Socket和ServerSocket类位于java.ne ...

  8. 关于BIO NIO和AIO的理解

    转载自 :http://blog.csdn.net/anxpp/article/details/51512200 1.BIO编程 1.1.传统的BIO编程 网络编程的基本模型是C/S模型,即两个进程间 ...

  9. Java IO、NIO、AIO知识总结

    本文主要讲述下自己对IO的理解,对IO的用法和细则可能没有顾虑到. 本文的理解基于以下几篇文章,他们对各自部分都讲的很细,对我理解IO提供了很大帮助. https://www.cnblogs.com/ ...

  10. IO、NIO和AIO的区别

      IO和NIO的区别:其本质就是阻塞和非阻塞的区别. 阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,那么久一直等着,知道传输完毕为止.非阻塞概念:应用程序直接可以获取已经准备就绪好的 ...

随机推荐

  1. beego orm 忽略字段

    忽略字段 设置 - 即可忽略 struct 中的字段 type User struct { ... AnyField string `orm:"-"` ... } beego or ...

  2. 通过Tag标签回退版本修复bug

    tag是对历史一个提交id的引用,如果理解这句话就明白了使用git checkout tag即可切换到指定tag,例如:git checkout v0.1.0 切换到tag历史记录会处在分离头指针状态 ...

  3. [Functional Programming] Functional JS - Pointfree Logic Functions

    Learning notes. Video. Less than: If you use 'ramda', you maybe know 'lt, gt'.. R.lt(2, 1); //=> ...

  4. CSS3制作图形大全——碉堡了

    为方便观看效果图,请移步原文:https://www.jqhtml.com/8045.html Square   #square {     width: 100px;     height: 100 ...

  5. CentOS5.5上安装Python2.7及ez_setup和pip包

    CentOS5.5上安装Python2.7及ez_setup和pip包 下载 首先从Python官方下载源代码包下载 编译安装 这里将python安装到/opt/python27文件夹下 tar xv ...

  6. Protobuf3 编解码

    我们已经基本能够使用Protocol Buffers生成代码,编码,解析,输出及读入序列化数据.该篇主要讲述PB message的底层二进制格式.不了解该部分内容,并不影响我们在项目中使用Protoc ...

  7. 如何配置JVM系统属性及获取方式System.getProperty("pname")

    https://www.cnblogs.com/keyi/p/7721893.html

  8. C语言 · 组合数

    组合数 从4个人中选2个人参加活动,一共有6种选法. 从n个人中选m个人参加活动,一共有多少种选法?下面的函数实现了这个功能. 请仔细分析代码,填写缺少的部分(下划线部分). 注意:请把填空的答案(仅 ...

  9. 匿名函数gc分析

    测试一:使用member function创建action会产生gc,不管该函数是否访问外部变量: private System.Action memberAct = null; // gc 112B ...

  10. java maven 编译文件时 有些类型文件 不存在

    在pom.xml中添加如下: <build> <resources> <resource> <directory>src/main/resource&l ...