Asyncdb(二):Java IO 初探

本文由 GodPan 发表在 ScalaCool 团队博客。java

Java IO对大多数Java程序员来讲是熟悉又陌生,熟悉的是感受处处都有它的身影,小到简单的读取文件,大到各类服务器的应用,陌生的是Java IO背后究竟是一个怎样的机制,今天就让咱们去了解一下这位老朋友吧。本文不讲解Java IO如何具体使用,有这方面需求的同窗能够本身查下。程序员

IO 模型

要说IO,就不得不说IO模型,IO模型你们都有所了解,同步异步,阻塞非阻塞什么的,总的来讲IO模型可分为如下五种:数据库

  • 阻塞IO
  • 非阻塞IO
  • 多路复用IO
  • 信号驱动IO
  • 异步IO

那么这几种IO都有什么区别呢?下面咱们一一来看,每种模型我都会举一个适当的例子助于理解:编程

1.阻塞 IO

阻塞IO相信你们都最熟悉了,线程发起一个IO请求,直到有结果返回,不然则一直阻塞等待,好比咱们日常常见的阻塞数据库操做,网络IO等。服务器

小明阻塞IO吃饭:网络

五年前一天周末,小明和朋友一块儿去商场的外婆家吃饭,到店后发现排队的人超多,因此他就领了一个号码,而后他和朋友就坐在旁边等候,一直等着服务员叫他们的号,也不能作其余事,过了一个多小时终于轮到他们了,而后他们进店点菜,又得等待上菜,最后他们吃饭总共花了两个小时;多线程

关键部分:异步

  • 等待座位吃饭:一直阻塞,直到有座位
  • 等待上菜:一直阻塞,直到有菜(假设菜上齐了再吃)

没什么说的,反正就是一直等,反应到程序中就是一直阻塞,而一个IO请求须要一个线程,可想而知当有大量的IO请求,线程的建立和销毁,线程间的切换,线程所占用的资源等等要耗费多少时间和资源,系统的性能会有多差。函数

2.非阻塞 IO

非阻塞IO和阻塞IO的最大区别就在于线程发起一个IO请求,不会一直堵塞直到有数据,而是不断的检查是否已有数据,如有数据则读取数据。工具

小明非阻塞IO吃饭:

有了第一次的教训,小明学乖了,他在拿到后再也不傻傻的等着,而是去外婆家旁边逛了逛,每过3分钟他就会回来,而后跑到前台去询问服务员轮到他了吗?不幸的是,排队的人超多,直到过了半个多小时后才轮到他进店吃饭,期间他大概问了十几回,他们进店点菜,又得等待上菜,最后他们吃饭总共花了两个小时,基本也没作啥其余事;

关键部分:

  • 领号后询问是否轮到他:非阻塞,非询问期间能够作点别的事,但也不作了啥大事
  • 等待上菜:一直阻塞,直到有菜(假设菜上齐了再吃)

总的来讲非阻塞IO的非阻塞主要体如今不须要一直等待到有数据,固然读数据那部分操做仍是阻塞的,另外这种非阻塞模式须要用户线程本身不断询问检查,其实效率也不是过高,实际编程中运用的也很少。

3.多路复用 IO

既然上面咱们说到非阻塞IO的缺点,那么有没有什么方式改进呢?答案是固然有,那就是多路复用IO,我理解的它的特色就是复用,首先它也是一种非阻塞IO的模型,只不过上面说到轮询的方式用了不一样的方式处理了,当一个线程发起IO请求,系统会将它注册到一个单独管理IO请求的一个线程,以后该IO的相关操做的通知状态都有这个管理IO请求的线程处理,Java 1.4发布的NIO就是这种模式,咱们能够大体来看一下它的流程:

// 打开服务器套接字通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 服务器配置为非阻塞
ssc.configureBlocking(false);
// 进行服务的绑定
ssc.bind(new InetSocketAddress("localhost", 8008));
// 这里的selector就至关于单独管理IO请求的线程
Selector selector = Selector.open();
// 注册到selector,等待链接
ssc.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select();  //为IO请求去轮询状态
    Set<SelectionKey> keys = selector.selectedKeys(); //多个IO请求的状态
    Iterator<SelectionKey> keyIterator = keys.iterator();
    while (keyIterator.hasNext()) { //依次处理IO请求
        SelectionKey key = keyIterator.next();
        doThing(key)
        ...
    }
}
复制代码

能够看出Java NIO的模式就是多路复用IO模型的应用。

小明多路复用IO吃饭:

随着生意愈来愈好,外婆家发现好多顾客都堵在门口等待吃饭,等待区都站不下来人了,,思来想去,外婆家准备请一我的专门来维护顾客的排队请求,这样顾客取号后,就不用堵在门口了,咱们叫他小A,小明此次取号后,将本身的相关信息告诉小A,并从小A那里得到了一个GPS(用于小A能快速找到小明,假设有了GPS后,小A能秒速找到小明),而后小明就跟朋友们开心的去逛商场,看看MM,买买衣服,而小A则不断的观察店里的状况,当有空座位出现的时候,他便会按照相关信息找到具体的顾客,将其带回进行用餐,但他们进店点菜,还得等待上菜,最后他们吃饭总共花了两个小时,可是他们再也不须要排队等位,而是去作一些其余的事。

关键部分:

  • 领号后委托给小A,小A观察到有空位后带回小明:非阻塞,领号后能够安心去作本身的事,不用担忧错过
  • 等待上菜:一直阻塞,直到有菜(假设菜上齐了再吃)

多路复用IO能够当作普通非阻塞IO的升级版,也是目前Java编程中用到比较多的IO模型,它的优点在于能够处理大量的IO请求,用一个线程管理全部的IO请求,无需像阻塞IO和非阻塞IO同样,每一个IO须要一个线程处理,提高了系统的吞吐量。

4.信号驱动 IO

信号驱动IO相对于以上几种模型最大的特色就是它支持内核信号通知,线程在发起一个IO请求后,会注册一个信号函数,而后内核在确认数据可读了,便会给相应的线程发送通知,让其进行具体IO读写操做。

小明信号驱动IO吃饭:

又了一段时间,外婆家经过使用复用IO模式缓解了排队拥挤的状况,可是以为还要请一我的专门维护队列,感受不划算,那么有没有一种更好的方式呢?通过一天的苦思冥想,外婆家的经理又想出一个好办法,让每一个顾客在领完号后,关注一下外婆家的公众号,而后顾客就能够去作别的事了,定时或者当排队信息发生改变时给顾客发送通知,告知他如今的排队序号或者轮到他吃饭了,顾客能够根据相应的信息作相应的行为,好比快轮到了就开始往店里走(实际程序中并不必定有这种状态,这里只是大概模拟),或者轮到本身了而后进店吃饭,他们仍然不用排队等位,而是去作一些其余的事。

关键部分:

  • 领号后关注公众号,注册关系:非阻塞,领号后能够安心去作本身的事,不用担忧错过
  • 等待上菜:一直阻塞,直到有菜(假设菜上齐了再吃)

就实际来讲,信号驱动IO用的并很少,由于信号驱动IO底层是使用SIGIO信号,因此它主要使用在UDP协议上,由于UDP产生SIGIO信号的时候只有两种可能:

  • 1.要么数据到达
  • 2.发生错误

但相对TCP来讲,产生SIGIO信号的地方太多了,好比请求链接,确认,断开,错误等等,因此咱们很难根据SIGIO信号判断到底发生了什么。

5.异步 IO

以上四种IO其实都仍是同步IO,由于它们在读写数据时都是阻塞的,异步IO相较于它们最大的特色是它读写数据的时候也是非阻塞的,用户线程在发起一个IO请求的时候,除了给内核线程传递具体的IO请求外,还会给其传递数据缓冲区,回调函数通知等内容,而后用户线程就继续执行,等到内核线程发起相应通知的时候,说明数据已经准备就绪,用户线程直接使用便可,无需再阻塞从内核拷贝数据到用户线程。

小明异步IO吃饭:

有过了一段时间,小明又想吃外婆家了,可是这个周末他并不想出门,他忽然在网上看到新闻说外婆家居然能够叫外卖,小明高兴坏了,他立刻打电话给外婆家,告诉它本身想要吃哪些菜(至关于IO请求所须要的数据),而后将本身的联系号码(至关于回调通知)和住址(至关于数据缓冲区)也告诉它,而后就挂掉电话,开心的作去打游戏了,过了半个小时后,手机响起,告知外卖已经到了,小明开门取外卖就能够直接开吃了。整个过程小明直到吃饭都没有等待阻塞。

关键部分:

  • 叫外卖并提供相应的信息:非阻塞,打完电话后作本身的事
  • 通知外卖到了:直接开门取外卖直接开吃,非阻塞

咱们能够看出,异步IO才是真正的异步,由于它连数据拷贝这个过程都是非阻塞的,用户线程根本不用关心数据的读写等操做,只需等待内核线程通知后,直接处理数据便可,固然异步IO须要系统内核支持,好比Linux中的AIO和Windows中的IOCP,可是也能够经过多线程跟阻塞I/O模拟异步IO,好比能够在多路复用IO模型上进行相应的改变,另外也有现有的实现,好比异步I/O的库:libeio

最后用一张图整体归纳一下Java IO(图片来自美团技术博客):

Java IO概图:

java-io

多路复用 IO 在 Linux 中的实现

由于后续会讲到Java NIO,因此咱们须要了解操做系统是如何支持多路复用IO的,Linux中支持支持三种多路IO复用机制,分别是select、poll和epoll,原本这里我想本身写的,但查阅了相应的一些资料后,发现本身的水平仍是不够,这里我不许备班门弄斧了,由于我找到了不少写的比较好的文章,这里就给你们列一下,仅供参考:

总结

这篇文章主要讲了最基础的IO模型,不过我认为最基础的每每是最重要的,只有理解了基础的原理,才能对基于它们实现的类库或者工具备更加深入的认识,下一篇文章将会主要讲一下基于多路复用IO的Java NIO,敬请期待。

相关文章
相关标签/搜索