BIO NIO AIO之间的区别

一、BIO、NIO、AIO的基本定义与类比描述:
BIO (Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
NIO (New I/O):同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
AIO ( Asynchronous I/O):异步非阻塞I/O模型。异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了
 
 
二、进程中的IO调用步骤大致可以分为以下四步:
1.进程向操作系统请求数据 ;
2.操作系统把外部数据加载到内核的缓冲区中;
3.操作系统把内核的缓冲区拷贝到进程的缓冲区 ;
4.进程获得数据完成自己的功能 ;
 
当操作系统在把外部数据放到进程缓冲区的这段时间(即上述的第二,三步),如果应用进程是挂起等待的,那么就是同步IO,反之,就是异步IO,也就是AIO 。
 
三、各自的特点
1)BIO(Blocking I/O)同步阻塞I/O 
    会阻塞每一个线程
 
    在高并发的web或者tcp服务器中采用BIO模型就无法应对了,如果系统开辟成千上万的线程,那么CPU的执行时机都会浪费在线程的切换中,使得线程的执行效率大大降低。
 
2)NIO (New I/O) 同步非阻塞I/O 
    只阻塞一个线程
 
NIO是New I/O的简称,与旧式的基于流的I/O方法相对,从名字看,它表示新的一套Java I/O标 准。它是在Java 1.4中被纳入到JDK中的,并具有以下特性:
– NIO是基于块(Block)的,它以块为基本单位处理数据
– 为所有的原始类型提供(Buffer)缓存支持
– 增加通道(Channel)对象,作为新的原始 I/O 抽象
– 支持锁和内存映射文件的文件访问接口
 – 提供了基于Selector的异步网络I/O
 
关于NIO弄清除 Channel、Buffer、Selector三个类之间的关系就可以了
 
Channel
首先说一下Channel,国内大多翻译成“通道”。Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream。而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作,NIO中的Channel的主要实现有:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel;通过看名字就可以猜出个所以然来:分别可以对应文件IO、UDP和TCP(Server和Client)。
 
Buffer
NIO中的关键Buffer实现有:ByteBuffer、CharBuffer、DoubleBuffer、 FloatBuffer、IntBuffer、 LongBuffer,、ShortBuffer,分别对应基本数据类型: byte、char、double、 float、int、 long、 short。
 
Selector
Selector 是NIO相对于BIO实现多路复用的基础,Selector 运行单线程处理多个 Channel,如果你的应用打开了多个通道,但每个连接的流量都很低,使用 Selector 就会很方便。例如在一个聊天服务器中。要使用 Selector , 得向 Selector 注册 Channel,然后调用它的 select() 方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等。
 
 1 import java.io.IOException;
 2 import java.net.InetSocketAddress;
 3 import java.nio.channels.SelectionKey;
 4 import java.nio.channels.Selector;
 5 import java.nio.channels.ServerSocketChannel;
 6 import java.util.Iterator;
 7  
 8 public class TCPServerSelector{
 9     //缓冲区的长度
10     private static final int BUFSIZE = 256;
11     //select方法等待信道准备好的最长时间
12     private static final int TIMEOUT = 3000;
13     public static void main(String[] args) throws IOException {
14         if (args.length < 1){
15             throw new IllegalArgumentException("Parameter(s): <Port> ...");
16         }
17         //创建一个选择器
18         Selector selector = Selector.open();
19         for (String arg : args){
20             //实例化一个信道
21             ServerSocketChannel listnChannel = ServerSocketChannel.open();
22             //将该信道绑定到指定端口
23             listnChannel.socket().bind(new InetSocketAddress(Integer.parseInt(arg)));
24             //配置信道为非阻塞模式
25             listnChannel.configureBlocking(false);
26             //将选择器注册到各个信道
27             listnChannel.register(selector, SelectionKey.OP_ACCEPT);
28         }
29         //创建一个实现了协议接口的对象
30         TCPProtocol protocol = new EchoSelectorProtocol(BUFSIZE);
31         //不断轮询select方法,获取准备好的信道所关联的Key集
32         while (true){
33             //一直等待,直至有信道准备好了I/O操作
34             if (selector.select(TIMEOUT) == 0){
35                 //在等待信道准备的同时,也可以异步地执行其他任务,
36                 //这里只是简单地打印"."
37                 System.out.print(".");
38                 continue;
39             }
40             //获取准备好的信道所关联的Key集合的iterator实例
41             Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
42             //循环取得集合中的每个键值
43             while (keyIter.hasNext()){
44                 SelectionKey key = keyIter.next();
45                 //如果服务端信道感兴趣的I/O操作为accept
46                 if (key.isAcceptable()){
47                     protocol.handleAccept(key);
48                 }
49                 //如果客户端信道感兴趣的I/O操作为read
50                 if (key.isReadable()){
51                     protocol.handleRead(key);
52                 }
53                 //如果该键值有效,并且其对应的客户端信道感兴趣的I/O操作为write
54                 if (key.isValid() && key.isWritable()) {
55                     protocol.handleWrite(key);
56                 }
57                 //这里需要手动从键集中移除当前的key
58                 keyIter.remove();
59             }
60         }
61     }
62 }

 

 
3)AIO (Asynchronous I/O) 异步非阻塞I/O 
    不阻塞任何线程
 
  • 读完了再通知我
  • 不会加快IO,只是在读完后进行通知 
  • 使用回调函数,进行业务处理
 
 
 1 public static void main(String[] args) throws IOException {
 2  
 3     AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0000));
 4     server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
 5         final ByteBuffer buffer = ByteBuffer.allocate(1024);
 6  
 7         @Override
 8         public void completed(AsynchronousSocketChannel result, Object attachment) {
 9             System.out.println(Thread.currentThread().getName());
10             Future<Integer> writeResult = null;
11             try {
12                 buffer.clear();
13                 result.read(buffer).get(100, TimeUnit.SECONDS);
14                 buffer.flip();
15                 writeResult = result.write(buffer);
16             } catch (InterruptedException | ExecutionException e) {
17                 e.printStackTrace();
18             } catch (TimeoutException e) {
19                 e.printStackTrace();
20             } finally {
21                 try {
22                     server.accept(null, this);
23                     writeResult.get();
24                     result.close();
25                 } catch (Exception e) {
26                     System.out.println(e.toString());
27                 }
28             }
29         }
30  
31         @Override
32         public void failed(Throwable exc, Object attachment) {
33             System.out.println("failed: " + exc);
34         }
35     });
36  
37 }

 

 
 
 
posted @ 2018-06-14 00:50  N!CE波  阅读(1818)  评论(0编辑  收藏  举报