粗暴的解释
从JDK 1.0开始,用户就可以通过Socket进行网络编程。在JDK 1.4后,又提供了一种全新的编程方式。
在翻阅文档的时候,相信不少人对“多路复用”这个词感到陌生和费解。
对于程序员来说,代码是更容易理解。
BIO示例:
ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//建立线程池
ServerSocket serverSocket = new ServerSocket();//新建Socket
serverSocket.bind(8088);//监听8088端口
while(!Thread.currentThread.isInturrupted()){
Socket socket = serverSocket.accept();//循环等待新连接进入
executor.submit(new ConnectIOnHandler(socket));//放入线程池中处理读写任务
}
class ConnectIOnHandler extends Thread{
private Socket socket;
public ConnectIOnHandler(Socket socket){
this.socket = socket;
}
public void run(){
while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){
String someThing = socket.read();//读取对方发来的消息
if(someThing!=null){
//TODO 做业务处理
socket.write();//返回结果
}
}
}
}
NIO 示例:
class IoThread extends Thread{
public void run(){
Set<SelectionKey> selectedKeys= selector.selectedKeys();
//循环获取网络事件。
Iterator<SelectionKey> i = selectedKeys.iterator();
for (;;) {
final SelectionKey k = i.next();
final Object a = k.attachment();
i.remove();
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
}
}
代码分析
在OIO中,将新连接放入到线程池中,利用多线程处理实际的读写任务。其中Socket的accept、read、write皆为阻塞操作。可以简单的认为每一个Socket连接对应了操作系统的一个小文件,对Socket的读写就是对这个文件的读写。这是一路一线程,或者是多路一线程的概念。
而在NIO中,通过Selector将Channel和event统一管理了起来。一次selector操作可以获取一个或者多个网络连接中的读写事件。这便是是多路复用的由来,它可以同时对多个网络连接进行操作。
为何引入NIO
在OIO编程模式中,通过线程池的引入,解决了OIO阻塞而引发的并行效率问题。看似已经美好,但是互联网的发展和用户量的提升,这种古老的编程模式遭受了挑战。
在OIO中,很难提升并发量。每一个新连接都需要一个Socket对象,虽然共用了线程池,但是为了保证响应性,需要设置较大的线程数。由于线程资源本身昂贵,过量的线程又会导致CPU资源竞争激烈,降低服务性能。OIO的特性决定了它必然无法支撑较大的并发量。
在NIO中,通过多路复用机制,解决了需要非常多的线程专注IO读写的问题,降低了服务的负载。从而提升了服务器的并发量。
多路复用使得服务的并发性上升了一个台阶,进一步的提升还有一个挑战。下一章将对此进行详细阐述。
来源:oschina
链接:https://my.oschina.net/u/992559/blog/1632539