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 }
你投入得越多,就能得到越多得价值