当前位置: 首页 > 科技观察

还是不懂JavaNIO?快来看看这篇文章

时间:2023-03-20 18:03:21 科技观察

首先,我们需要明确几个概念:同步和异步,阻塞和非阻塞。同步与异步1、当同步进程触发IO操作时,必须自己处理;例如,您必须亲自去银行取款。2、异步进程触发IO操作时,不需要自己去处理。它将操作委托给操作系统。委托的时候需要告知数据的地址和大小,其他的就自己做。当IO操作完成时,会得到通知;比如你把你的银行卡给我,让我帮你去银行取钱,你要告诉我银行卡密码,你要取多少钱,我给你钱后退出。3、总结一下,你做的是同步的,别人做的是异步的。阻塞与非阻塞1.当阻塞进程触发IO操作时,如果此时没有办法读写,进程会一直等到读写结束;你必须等到你取完钱;2、非阻塞进程触发IO操作时,如果此时没有办法读写,那就先做其他事情,等有通知的时候再继续读写;比如去银行柜台取钱,人多,那就先拿号,等叫号,再去对应的窗口办理业务;这里有点不合适的是,在我们等待的时候,我们要听电话号码。3.总结一下,如果我要等待不能做其他事情,就是阻塞,如果我不用等待就可以做其他事情,就是异步。BIO被同步阻塞;当有请求到来时,应用开启一个线程,当IO准备好后,IO操作也由自己完成;采用BIO模型的服务器由一个独立的Acceptor线程进行监控;在while(true)循环中调用accept()方法,等待客户端的请求;一旦收到请求,就可以建立socket开始读写操作,此时不会再收到其他请求,直到读写完成;为了让BIO处理多个Request,那么就需要使用多线程处理;服务端收到请求后,创建一个线程供客户端处理,处理完成后销毁该线程;但是因为一个请求需要启动一个线程,开销比较大是的,启动和销毁线程是昂贵的,而且每个线程都占用内存,所以可以引入线程池,将线程创建和销毁的开销降低到一定程度;这也称为伪异步IO。线程池维护了N个线程和一个消息队列;当访问请求时,服务器将Socket作为参数传递给线程任务进行处理;通过控制线程池的最大线程数和消息队列的大小,即使访问量高于服务器的承载能力,也不会因为服务器资源耗尽而导致宕机;在请求量不高的时候,这个模型的效率还是不错的,不需要考虑限流的问题(控制线程池的最大线程数)。NIO是同步和非阻塞的;无需等待IO准备好,准备好后通知,但IO操作还是要自己完成;NIO是一种多路复用机制,使用单线程轮询事件,Channel来决定做什么,避免了连接数过多的时候,频繁的线程切换导致性能问题(阻塞在Select阶段)。听到这里,可能很多人都一头雾水……什么是复用?什么是频道?Select阶段到底是什么?这里我用白话解释一下。NIO是面向缓冲区的,数据可以读入缓冲区并在以后处理。NIO有几个核心概念:1.Channel和BufferChannel可以理解为双向流,也可以理解为通道,Buffer是缓冲区,也可以认为是内存空间,数据可以从Channel流向Buffer,也可以从Buffer流向Channel。Channel的实现有很多种,例如:FileChannel从文件中读写数据,SocketChannel通过TCP在网络中读写数据等等。Buffer也有多种类型,比如:ByteBuffer、CharBuffer、IntBuffer等,光看它们的名字就可以知道它们代表的是不同的数据类型。2.Selector我们可以把Selector看成是一个可以管理多个Channel的管理员。Selector可以知道哪个Channel准备好读写了。这样的线程只需要操作administrator,相当于一个线程可以管理多个Channel;一旦监听到一个就绪的Channel,就可以对其进行相应的处理。但是,直到Netty出现,Java原生的NIO才开始好用。AIO是异步非阻塞的;因为事情不是自己做的,所以其实不存在阻塞(都是非阻塞);AIO是在NIO的基础上,引入了异步通道的概念;NIO采用轮询的方式不断的去请求数据,不管是否准备好,准备好了就去处理;AIO是向操作系统注册IO监听,操作系统完成IO操作后,会主动通知并触发响应函数(自己不要做,让操作系统做)。目前AIO应该还没有广泛使用。看完这篇文章,你是不是对BIO、NIO、AIO有了初步的了解呢?