1.IO操作包括:对硬盘的读写、对socket的读写以及外设的读写。
2.IO请求操作的两个步骤:查看数据是否就绪;进行数据拷贝(内核将数据拷贝到用户线程)
3.阻塞和非阻塞的区别在第一阶段:如果数据没有就绪,在查看数据是否就绪的过程中是一直等待还是直接返回一个标志信息。但两者都是同步IO,非阻塞IO中,如果kenel的数据没有准备好,这个时候不会block进程,但是,当kenel中数据准备好的时候,系统调用会将数据从kenel拷贝到用户内存中,这个时候进程是被block的。
4.Java中传统的IO都是阻塞IO,比如通过socket来读数据,调用read()方法之后,如果数据没有就绪,当前线程就会一直阻塞在read方法调 用那里,直到有数据才返回;而如果是非阻塞IO的话,当数据没有就绪,read()方法应该返回一个标志信息,告知当前线程数据没有就绪,而不是一直在那 里等待。
5. 异步IO:只有IO请求操作的发出是由用户线程来进行的,IO操作的两个阶段都是由内核自动完成,然后发送通知告知用户线程IO操作已经完成。也就是说在异步IO中,不会对用户线程产生任何阻塞。
同步IO和异步IO关键区别:数据拷贝阶段是由用户线程完成还是内核完成。所以说异步IO必须要有操作系统的底层支持。
阻塞IO和非阻塞IO是反映在IO操作的第一个阶段,在查看数据是否就绪时是如何处理的。
6.阻塞IO,用户线程交出CPU
1 data = socket.read();
7.非阻塞IO,用户线程不交出CPU
1 while(true){ 2 data = socket.read(); 3 if(data!= error){ 4 处理数据 5 break; 6 } 7 }
但是对于非阻塞IO就有一个非常严重的问题,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率非常高,因此一般情况下很少使用while循环这种方式来读取数据。
8.多路复用IO模型是目前使用得比较多的模型。Java NIO实际上就是多路复用IO。
有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。
在Java NIO中,是通过selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在那里,因此这种方式会导致用户线程的阻塞。当任何一个socket中的数据准备好了,select就会返回,这个时候用户线程在调用实际的IO操作,将数据从kenel拷贝到用户线程。
9.异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到 用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就说用户线程完全不需要实际的整个IO操作是如何进行的,只 需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。
异步IO是需要操作系统的底层支持,在Java 7中,提供了Asynchronous IO。
10.传统的网络服务设计模式 多线程 线程池 前者资源使用瓶颈 后者对于长连接,在一段时间内,线程池中的线程都被占用,再有连接,连接失败。
11.各个IO 模型比较示意图
12.高性能IO设计模式:
Reactor模式(同步IO):会先对每个client注册感兴趣的事件,然后有一个线程专门去轮询每个client是否有事件发生,当有事件发生时,便顺序处理每个事件(为了提高速度,可以通过多线程或者线程池的方式处理),当所有事件处理完之后,便再转去继续轮询。
多路复用IO就是这个模式
12.Proactor模式(异步IO):当检测到有事件发生时,会新起一个异步操作,然后交由内核线程去处理,当内核线程完成IO操作之后,发送一个通知告知操作已完成,可以得知,异步IO模型采用的就是Proactor模式。
参考文献:
http://www.cnblogs.com/dolphin0520/p/3916526.html
http://blog.csdn.net/historyasamirror/article/details/5778378