前言
进行总结的初衷是没搞明白linux下的select/poll/epoll与java下nio的selector多路复用器的关系,于是对知识点进行了梳理。
先上结论,selector多路复用器算是对linux下的select/poll/epoll进行封装,selector可以i有多种实现,linux系统下默认使用epoll的实现方式。
BIO模型
https://www.jianshu.com/p/138847d5cafd
BIO即为阻塞IO的意思,通常我们讲BIO的时候都会和服务器模型配合着讲,在实际应用中讲会更好理解。大家看下面的代码,估计在大家初学java网络编程的时候用的都是这个模型:
什么是BIO,NIO?他们和多路复用器有啥关系?
只要没有客户端连接上服务器,accept方法就一直不能返回,这就是阻塞;对应的读写操作道理也一样,想要读取数据,必须等到有数据到达才能返回,这就是阻塞。
我们还可以站在阻塞的基础上思考一下,为什么服务器的模型要设计成来一个客户端就新建一个线程?
其实答案很简单,当来了一个客户端创建连接后,如果不给客户端新分配一个线程执行服务器逻辑,那么服务端将很难再和第二个客户端建立连接。
就算你把客户端连接用集合保存起来,通过单线程遍历集合的方式去执行服务器端逻辑也是不行的。因为如果某个客户端连接因为读写操作阻塞了,那么其他客户端将得不到执行。
优点:
每个连接创建一个线程,实现了一个服务器连接多个客户端
缺点:
1.每处理一个请求就要new一个线程,而cpu是不会停止的,时间片轮调度各个线程切换(系统计时器发出时钟中断,调度程序停止该进程运行并把进程放到就绪队列末尾),上下文切换浪费时间,并且线程多消耗内存。并且太多系统调用(用户态到内核态)。系统调用:socket监听端口,客户端accpet,创建线程,客户端读取都是系统调用
2.很多线程其实是在等数据(阻塞),是一个无用的线程
没用多路复用的NIO模型
如果说服务器只有很少的人用,那么上面那段bio的代码其实挺好的,但问题在于互联网蓬勃发展,随着服务器访问人数的增加,这样的服务器模型将会成为瓶颈。
我们以一种C10K的思想去看待上面这段服务器代码。如果我们客户端的连接数增加了10K倍,那么就意味着要创建10k个线程,单单创建线程就是一项不小的开销了,再加上线程之间要来回切换,单机服务器根本就扛不住这么大的连接数。
那既然瓶颈是出在线程上,我们就考虑能不能把服务器的模型变为单线程模型,思路其实和之前说的差不多,用集合保存每个连接的客户端,通过while循环来对每个连接进行操作。
之前我们说了这样的操作瓶颈在于accept客户端的时候会阻塞,以及进行读写操作的时候会阻塞,导致单线程执行效率低。为了突破这个瓶颈,操作系统发展出了nio,这里的nio指的是非阻塞io。
也就是说在accept客户端连接的时候,不需要阻塞,如果没有客户端连接就返回-1(java-NULL),在读写操作的时候,也不阻塞,有数据就读,没数据就直接返回,这样就解决了单线程服务器的瓶颈问题。示例代码如下:
优点:
相比于BIO&#x