概念
BIO 阻塞io,1.4之前
NIO no-blocking io 非阻塞io,jdk1.4
AIO 异步io,jdk1.7
浏览器输入网址,敲下回车之后发生了什么?
1.URL解析
2.DNS解析
概念:Domain Name System,域名系统,本质上是一个分布式数据库。将人类可读的域名解析成计算机可读的IP地址
解析顺序:从右向左
域名的层级:类似于索引,一级一级更有效率的查找
DNS查询的两种方式:递归与迭代
网络协议
各个层的数据包格式
一个数据包叫一个帧,最大1518字节,经过每个层的时候,会加上特定的标头信息
java.io专业术语
字符流:相对高级,处理人类可以理解和阅读的字符
基本字符流:
高级字符流:
字节流:相对低级,处理字节,1字节=8bit
基本字节流:
高级字节流:
设计模式:装饰者模式,高级流套在低级流之上
Socket
socket以ip+port作为唯一标识,与网卡驱动进行绑定,实现点对点的通信
unix系统中,一切皆是文件,socket也不例外,这就是为什么在多路复用模型中,总是会提到文件描述符这个词语了。
同步异步阻塞非阻塞
一个表白的故事,一个男生向心仪已久的女生表白
同步:女生想了一段时间,当场给出了自己的答复
异步:女生说,你让我考虑几天,想好了我会打你电话
阻塞:男生心心念念,茶不思饭不想,一心等待着女生的答复。
非阻塞:男生落落大方,个人生活并没有被表白这一件事情完全占据,等待回复这段时间,正常在做自己的事情,比如打打篮球
线程池
本质上为了线程复用,银行办理业务的故事
单线程:只有一个柜员,顾客排队办理业务。
多线程非线程池:有客户就招柜员,办理完了就辞退柜员,再有新的顾客再重新招聘柜员,这无疑是非常可笑的。
线程池:有一批固定的柜员,平时作为常设窗口提供服务(核心线程数),
业务繁忙的时候,由HR去招聘一批员工(线程工厂),
如果窗口已经全部开放(最大线程数),客户仍然很多,就需要在大厅排队办理(工作队列),
如果业务非常火爆(比如听过银行破产,储户争相取款- -),整个大厅挤满了客户,那么对于其他大厅外的客户,银行只能说no,告知暂时无法提供服务(拒绝策略)
业务高峰过去之后,新招的员工并不会被立即辞退,只有那些长时间没有业务的员工,才会被HR辞退(空闲时间)
java提供的线程池(除此之外 ,还有手动创建,ThreadpoolExecutor、ScheduledThreadpoolExecutor、Fork Join Pool):
JAVA IO的前世:BIO阻塞模型
JAVA IO的今生:NIO非阻塞模型
buffer解析
channel是读写双向的,依赖buffer来实现,通过flip()方法来实现读/写模式的翻转
写模式
读模式,limit移动到之前写模式的最后一条,position移动到起始位置。即只能读取写模式下写入到buffer中的数据
如果数据全部读取完,调用clear()方法重新转换成写模式,limit回到最末尾,position回到起始位置
clear()方法其实并没有真的去清除buffer中的数据,只是移动了两个指针而已,但是下次再写入的时候,原有数据就会被覆盖,效果上等同于清除,是一个很巧妙而高效的方法
如果数据并未全部读完,在这之前需要先转换成写模式,则调用compact()方法
compact()方法把之前未读完的数据复制到buffer的最前面,然后把position指向紧跟着的那个位置,从这个位置开始写入新的数据,limit移动到末尾,这样可以确保每次读的时候是从上一次未读取完的位置开始继续读取。
channel解析
channel之间可以直接进行数据交换
几个重要的channel
多方法实现文件拷贝
不带缓冲区的字节流拷贝
带缓冲区的字节流拷贝
基于channer的buffer进行拷贝
两个channel之间直接拷贝
性能对比:三个不同大小的文件,小的400K,中等的10M。大的500M,四个拷贝方法均执行5次,取平均值进行比较
不带缓冲区的字节流拷贝性能非常差,几千倍的差距
其他三种方法差别不大,随着文件的增大,nio的方法效率相对来说比传统的bufferedStream好一些
其实传统io的方法实现在新版本的jdk中已经被重写过了,在jdk1.4的时候,nio方法的性能要比传统io好很多,目前主流的jdk1.8中,底层实现差不多,所以性能没有太明显的差距了。
selector解析
所有的channel注册在selector上面,由selector来监测channel的状态变化
channel的状态变化
connect:socketChannel连接到了服务器上
accept:serverSocketChannel接受了一个连接
read:channel上有数据,处于可读状态
write:channel处于可写入状态
在Selector上注册channel
每个channel获得一个唯一的SelectionKey,interestOps()关心的事件,readyOps()就绪的事件
使用selector选择channel
select()统计所有注册的channel事件就绪的个数,操作完之后需要手动的重置就绪状态,以便当channel再次就绪的时候selector可以正确的统计
NIO编程模型
socketServerChannel注册监听accept事件,响应说明有新的socketChannel建立了连接,然后对这个新的socketChannel在selector上注册监听read事件,当read事件就绪的时候(buffer中有可读数据),由selector所在的线程去处理该io请求
这样,selector所在的这个线程可以同时处理多个io请求,不像BIO模型中每个io请求都需要有一个单独的线程去一对一的阻塞处理
JAVA IO的后世之师:AIO异步模型
内核IO模型
阻塞式I/O:每次系统调用阻塞,知道成功返回数据为止
非阻塞式I/O:不停的进行系统调用,没有数据就直接返回无数据,知道有成功返回为止
I/O多路复用:不再由应用程序自身不停的进行系统调用,交由selector来监听事件,事件就绪的时候,会返回可读条件,然后应用程序发起系统调用,成功返回数据
异步I/O:前面三种都是同步调用,无论是阻塞还是非阻塞,应用程序必须主动发起系统调用才能得到数据,在异步I/O模型中,应用程序先发起系统调用,如果此时数据没有准备好,
则返回无数据(非阻塞),当未来的某个时间,内核将数据准备好了之后,就会执行相应的回调函数
异步调用机制
AIO中的异步操作
异步实现原理
基于Future,异步阻塞,用于客户端
基于CompletionHandler,异步非阻塞,用于服务端 ,底层有一个AsyncronousChannelGroup线程池,用来执行回调函数,性能比nio的单线程同步非阻塞selector更高,AIO模型的精髓也就在这里。
AIO编程模型
三种IO模型适用情境