BIO
最原始的BIO模型 实际上就是我们以前经常使用的阻塞模型 ,在socket.accept的时候 整个程序会在此处阻塞,在read的时候也会阻塞整个线程,这样的有点是可以即使处理所有的任务,但是缺点非常明显,如果出现了高并发的情况,我们的操作系统每接收一个连接就创建一个线程,要知道线程是会占用内存的,如果创建了很多线程就会导致内存爆掉,而且很多线程 那么操作系统线程间的切换就会耗费很长的时间。
public static void main(String[] args) throws Exception{
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("step1: new serverSocket(8080)");
while (true){
Socket accept = serverSocket.accept();
System.out.println("step2: client\t"+accept.getPort());
new Thread(()->{
InputStream inputStream=null;
try{
inputStream=accept.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
while (true){
String dataLine = bufferedReader.readLine();
if(dataLine!=null){
System.out.println(dataLine);
}else {
accept.close();
break;
}
}
System.out.println("客户端断开连接");
}catch (Exception e){
e.printStackTrace();
}
});
}
}
NIO
所以由此 出现了NIO模型,nio实际上他在程序中的调用和BIO很相似,都是创建一个套接字然后绑定端口,不同的是操作系统为我们提供的东西,操作系统为我们提供了一个NONBLOCK操作,本质上就是如果我们向操作系统发送一个accept或者read请求,操作系统不会阻塞,会直接返回给我们东西,如果有连接传输就返回连接 没有就直接返回连接失败,不会阻塞住我们的线程,这样我们的同一个线程就可以处理多个Socket:下面这个图一个是向操作系统内核设置为非阻塞的,另一个是socket.accept方法 可以看到我们没有连接 它没有阻塞 而是直接给了我们一个-1的返回值。
多路复用技术
我们NIO虽然解决了阻塞的问题 但是另一个问题出现了,如果没有阻塞,那么我们的线程会一个while循环一直调用,对CPU资源造成极大的损耗,那么此时出现了极为优秀的多路复用的技术,select poll epoll等都是多路复用的实现,如果我们的线程1负责 1-4号任务,那么当1号任务收到连接过后 他会取通知线程去处理它的数据,而不是线程的盲目轮询操作。 通过NIO和多路复用,操作系统帮我们做到了一个线程可以处理多个任务同时不会进行CPU资源的浪费。
epoll是对我们的select 和 poll等命令的增强,我们以前多路复用去提醒线程,每次提醒的时候都需要发送一次完整的参数过去所以发明了 epoll,有三个操作系统命令
select的命令
epoll:
创建内存空间
将任务添加进入复用器中
等待epoll给我们发送消息
在epoll中我们只需要添加一次任务进入epoll中即可