`

java io bio nio aio 详解

 
阅读更多

http://blog.csdn.net/liuj2511981/article/details/8658440

 

BIO、NIO、AIO的区别:
BIO就是基于Thread per Request的传统server/client实现模式,

NIO通常采用Reactor模式,

AIO通常采用Proactor模式,

AIO简化了程序的编写,stream的读取和写入都有OS来完成,不需要像NIO那样子遍历Selector。Windows基于IOCP实现AIO,Linux只有eppoll模拟实现了AIO。

Java7之前的JDK只支持NIO和BIO,从7开始支持AIO。

4种通信方式:TCP/IP+BIO, TCP/IP+NIO, UDP/IP+BIO, UDP/IP+NIO。

一、 Reactor and Proactor

IO读写时,多路复用机制都会依赖对一个事件多路分离器,负责把源事件的IO 事件分离出来,分别到相应的read/write事件分离器。涉及到事件分离器的两种模式分别就是 Reactor和Proactor,Reactor是基于同步IO的,Proactor是基于异步IO的。

关于同步和异步IO

Io的两个重要步骤:发起IO请求,和实际的IO操作。在unix网络编程的定义里异步和非异步概念的区别就是实际的IO操作是否阻塞。如果不是就是异步,如果是就是同步。
而阻塞和非阻塞的区别在于发起IO请求的时候是否会阻塞,如果会就是阻塞,不会就是非阻塞。

本人理解能力有限,想了个例子来辅助自己理解:
小明想要买一本<深入java虚拟机>的书,以下几个场景可以来理解这几种io模式:
1. 如果小明每天都去书店问售货员说有没有这本书,如果没有就回去继续等待,等下次再过来文。(阻塞)
2. 如果小明告诉售货员想买一本<深入java虚拟机>的书,那么就在家里等着做其他事情去了,如果书到了售货员就通知小明,小明再自己过去取。
3. 如果小明告售货员想买一本<深入java虚拟机>的书,然后告诉售货员到了帮他送到某某地方去,就做其他事情去了。小明就不管了,等书到了,售货员就帮他送到那个地方了。

售货员可以认为是操作系统的一个服务,而小明是一个用户进程。不知道是否有误,如果有误请大家拍砖指出,谢谢。
可以看出2,3的效率明显要比1高。但是1最简单,而2,3需要一些协作。充分证明了团队合作的力量。

在Reactor模式中,事件分离者等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分离者就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。

在Proactor模式中,事件处理者(或者代由事件分离者发起)直接发起一个异步读写操作(相当于请求),而实际的工作是由操作系统来完成的。发起时,需要提供的参数包括用于存放读到数据的缓存区,读的数据大小,或者用于存放外发数据的缓存区,以及这个请求完后的回调函数等信息。事件分离者得知了这个请求,它默默等待这个请求的完成,然后转发完成事件给相应的事件处理者或者回调。举例来说,在Windows上事件处理者投递了一个异步IO操作(称有 overlapped的技术),事件分离者等IOCompletion事件完成. 这种异步模式的典型实现是基于操作系统底层异步API的,所以我们可称之为“系统级别”的或者“真正意义上”的异步,因为具体的读写是由操作系统代劳的。

举个例子,将有助于理解Reactor与Proactor二者的差异,以读操作为例(类操作类似)。

在Reactor中实现读:

- 注册读就绪事件和相应的事件处理器

- 事件分离器等待事件

- 事件到来,激活分离器,分离器调用事件对应的处理器。

- 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

与如下Proactor(真异步)中的读过程比较:

- 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。

- 事件分离器等待操作完成事件

- 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。

- 事件分离器呼唤处理器。

- 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。

可以看出,两个模式的相同点,都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构

上,两者也有相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler;

不同点在于,异步情况下(Proactor),当回调handler时,表示IO操作已经完成;同步情况下(Reactor),回调handler时,表示

IO设备可以进行某个操作(can read or can write),handler这个时候开始提交操作。

 

二、BIO、NIO、AIO

NIO通常采用Reactor模式,AIO通常采用Proactor模式。AIO简化了程序的编写,stream的读取和写入都有OS来完成,不需要像NIO那样子遍历Selector。Windows基于IOCP实现AIO,Linux只有eppoll模拟实现了AIO。

Java7之前的JDK只支持NIO和BIO,从7开始支持AIO。

4种通信方式:TCP/IP+BIO, TCP/IP+NIO, UDP/IP+BIO, UDP/IP+NIO。

(1)TCP/IP+BIO

Socket和ServerSocket实现,ServerSocket实现Server端端口监听,Socket用于建立网络IO连接。不适用于处理多个请求 1.生成Socket会消耗过多的本地资源。2. Socket连接的建立一般比较慢。
BIO情况下,能支持的连接数有限,一般都采取accept获取Socket以后采用一个thread来处理,one connection one thread。无论连接是否有真正数据请求,都需要独占一个thread。
    可以通过设立Socket池来一定程度上解决问题,但是使用池需要注意的问题是:1. 竞争等待比较多。 2. 需要控制好超时时间。Socket和ServerSocket实现,ServerSocket实现Server端端口监听,Socket用于建立网络IO连接。

服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。

[java] view plaincopy
 
  1. package test;    
  2.     
  3. import java.io.BufferedReader;    
  4. import java.io.IOException;    
  5. import java.io.InputStreamReader;    
  6. import java.net.ServerSocket;    
  7. import java.net.Socket;    
  8.     
  9. public class Server {    
  10.     private Socket socket;    
  11.     private ServerSocket ss;    
  12.     
  13.     public Server() throws IOException {    
  14.         ss = new ServerSocket(7777);    
  15.         while (true) {    
  16.             socket = ss.accept();    
  17.             BufferedReader br = new BufferedReader(new InputStreamReader(socket    
  18.                     .getInputStream()));    
  19.             System.out.println("you input is : " + br.readLine());    
  20.         }    
  21.     }    
  22.     
  23.     public static void main(String[] args) {    
  24.         try {    
  25.             new Server();    
  26.         } catch (IOException e) {    
  27.             e.printStackTrace();    
  28.         }    
  29.     }    
  30. }    

客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。

[java] view plaincopy
 
  1. package test;    
  2.     
  3. import java.io.BufferedRear;    
  4. import java.io.IOException;    
  5. import java.io.InputStreamReader;    
  6. import java.io.PrintWriter;    
  7. import java.net.Socket;    
  8. import java.net.UnknownHostException;    
  9.     
  10. ublic class Client {    
  11.    Socket client;    
  12.    PrintWriter pw;    
  13.    public Client() throws UnknownHostException, IOException {    
  14.        client=new Socket("Socket服务器IP",7777);    
  15.        pw=new PrintWriter(client.getOutputStream());    
  16.        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));    
  17.        pw.write(br.readLine());    
  18.        pw.close();    
  19.        br.close();    
  20.    }    
  21.    public static void main(String[] args) {    
  22.        try {    
  23.            new Client();    
  24.        } catch (UnknownHostException e) {    
  25.            e.printStackTrace();    
  26.        } catch (IOException e) {    
  27.            e.printStackTrace();    
  28.     }    
  29.    }    
  30.     


不适用于处理多个请求 1.生成Socket会消耗过多的本地资源。2. Socket连接的建立一般比较慢。

BIO情况下,能支持的连接数有限,一般都采取accept获取Socket以后采用一个thread来处理,one connection one thread。无论连接是否有真正数据请求,都需要独占一个thread。

可以通过设立Socket池来一定程度上解决问题,

[java] view plaincopy
 
  1. import java.io.BufferedReader;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import java.io.InputStreamReader;  
  6.   
  7. import java.io.PrintWriter;  
  8.   
  9. import java.net.ServerSocket;  
  10.   
  11. import java.net.Socket;  
  12.   
  13. import java.util.concurrent.ExecutorService;  
  14.   
  15. import java.util.concurrent.Executors;  
  16.   
  17.    
  18.   
  19. public class BIOPoolServer {  
  20.   
  21. ExecutorService pool = null;  
  22.   
  23.     public  BIOPoolServer(){  
  24.   
  25.     try {  
  26.   
  27. ServerSocket server = new ServerSocket(29001);  
  28.   
  29. pool = Executors.newFixedThreadPool(1);  
  30.   
  31. while(true){  
  32.   
  33. pool.execute(new Handler(server.accept()));  
  34.   
  35. }  
  36.   
  37. catch (IOException e) {  
  38.   
  39. e.printStackTrace();  
  40.   
  41. }finally{  
  42.   
  43. pool.shutdown();  
  44.   
  45. }  
  46.   
  47.     }  
  48.   
  49.       
  50.   
  51.     class Handler implements Runnable{  
  52.   
  53.     Socket socket;  
  54.   
  55.     public Handler(Socket socket){  
  56.   
  57.     this.socket = socket;  
  58.   
  59.     }  
  60.   
  61. public void run() {  
  62.   
  63. try {  
  64.   
  65. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
  66.   
  67. PrintWriter out = new PrintWriter(socket.getOutputStream(),true);  
  68.   
  69.                 String msg = in.readLine();  
  70.   
  71.                 System.out.println("The client send the msg : "+msg);  
  72.   
  73.     out.println("The server has received!");  
  74.   
  75. catch (IOException e) {  
  76.   
  77. e.printStackTrace();  
  78.   
  79. }  
  80.   
  81. }  
  82.   
  83.     }  
  84.   
  85.     public static void main(String[] args) {  
  86.   
  87. new BIOPoolServer();  
  88.   
  89. }  
  90.   
  91. }  


但是使用池需要注意的问题是:1. 竞争等待比较多。 2. 需要控制好超时时间。

 

(2)TCP/IP+NIO

使用Channel(SocketChannel和ServerSocketChannel)和Selector。

Server端通常由一个thread来监听connect事件,另外多个thread来监听读写事件。这样做的好处是这些连接只有在真是请求的时候才会创建thread来处理,one request one thread。这种方式在server端需要支持大量连接但这些连接同时发送请求的峰值不会很多的时候十分有效。

 

[java] view plaincopy
 
  1. server端:  
  2.   
  3.    
  4.   
  5. import java.io.IOException;  
  6.   
  7. import java.net.InetSocketAddress;  
  8.   
  9. import java.nio.ByteBuffer;  
  10.   
  11. import java.nio.IntBuffer;  
  12.   
  13. import java.nio.channels.SelectionKey;  
  14.   
  15. import java.nio.channels.Selector;  
  16.   
  17. import java.nio.channels.ServerSocketChannel;  
  18.   
  19. import java.nio.channels.SocketChannel;  
  20.   
  21. import java.nio.channels.spi.SelectorProvider;  
  22.   
  23. import java.util.Iterator;  
  24.   
  25. import java.util.Set;  
  26.   
  27.    
  28.   
  29. public class NIOServer {  
  30. ServerSocketChannel channel = null;  
  31.    public NIOServer(){  
  32.   try {  
  33.   
  34.   openChannel();  
  35.   waitForConnection();  
  36.   
  37. catch (IOException e) {  
  38.   
  39. e.printStackTrace();  
  40.   
  41. }  
  42.   
  43.    }  
  44.    private void openChannel() throws IOException{  
  45. channel = ServerSocketChannel.open();  
  46. //绑定监听端口  
  47. channel.socket().bind(new InetSocketAddress(29000));  
  48. //设置为非阻塞形式  
  49. channel.configureBlocking(false);  
  50.   }  
  51.    private void waitForConnection() throws IOException{    
  52.   Selector acceptSelector = SelectorProvider.provider().openSelector();  
  53.   channel.register(acceptSelector, SelectionKey.OP_ACCEPT);  
  54.   int keyAdded = 0;  
  55.   while((keyAdded=acceptSelector.select())>0){  
  56.   
  57. // 某客户已经准备好可以进行I/O操作了,获取其ready键集合  
  58.   Set readKeys = acceptSelector.selectedKeys();  
  59.   Iterator iter = readKeys.iterator();  
  60.   while(iter.hasNext()){  
  61.   SelectionKey sk = (SelectionKey)iter.next();  
  62.   iter.remove();  
  63.   if(sk.isAcceptable()){  
  64.   ServerSocketChannel server = (ServerSocketChannel) sk.channel();  
  65.   SocketChannel socket = server.accept();  
  66.   ByteBuffer _buffer = ByteBuffer.allocate(8);    
  67.   IntBuffer _intBuffer = _buffer.asIntBuffer();   
  68.    _buffer.clear();    
  69.   
  70.    socket.read(_buffer);  
  71.        int result = _intBuffer.get(0) + _intBuffer.get(1);    
  72.        _buffer.flip();    
  73.        _buffer.clear();    
  74.        _intBuffer.put(0, result);    
  75.        socket.write(_buffer);  
  76.   
  77.   }  
  78.   }  
  79.   }  
  80.   
  81.    }  
  82.    public static void main(String[] args) {  
  83.   
  84.  new NIOServer();  
  85.   
  86. }  
  87.   
  88. }  
  89.   
  90.    
  91. client端:  
  92.   
  93.    
  94.   
  95. import java.io.IOException;  
  96.   
  97. import java.net.InetSocketAddress;  
  98.   
  99. import java.nio.ByteBuffer;  
  100.   
  101. import java.nio.IntBuffer;  
  102.   
  103. import java.nio.channels.SocketChannel;  
  104. public class NIOClient {  
  105.  public void start(int first, int second){  
  106. SocketChannel channel = null;  
  107. try {  
  108.   
  109. InetSocketAddress socketAddress = new InetSocketAddress("localhost"29000);   
  110. channel = SocketChannel.open(socketAddress);  
  111. channel.configureBlocking(false);  
  112. ByteBuffer _buffer = ByteBuffer.allocate(8);   
  113. IntBuffer _intBuffer = _buffer.asIntBuffer();  
  114. _buffer.clear();    
  115. _intBuffer.put(0, first);    
  116. _intBuffer.put(1, second);    
  117. channel.write(_buffer);   
  118. System.out.println("发送加法请求 " + first + "+" + second);  
  119. _buffer.clear();    
  120.   
  121.     channel.read(_buffer);   
  122.     int result = _intBuffer.get(0);   
  123.     System.out.println("运算结果:"+result);  
  124. catch (IOException e) {  
  125. e.printStackTrace();  
  126.   
  127. }finally {    
  128.   
  129.         if (channel != null) {    
  130.   
  131.             try {    
  132.   
  133.             channel.close();    
  134.   
  135.             } catch (IOException e) {    
  136.   
  137.             }    
  138.   
  139.         }    
  140.   
  141.     }      
  142.   
  143.  }  
  144.   
  145.  public static void main(String[] args) {  
  146.   
  147. new NIOClient().start(323);  
  148.   
  149. }  
  150.   
  151. }  


使用Channel(SocketChannel和ServerSocketChannel)和Selector。

Server端通常由一个thread来监听connect事件,另外多个thread来监听读写事件。这样做的好处是这些连接只有在真是请求的时候才会创建thread来处理,one request one thread。这种方式在server端需要支持大量连接但这些连接同时发送请求的峰值不会很多的时候十分有效。

 

(3)UDP/IP+BIO

DatagramSocket和DatagramPacket。DatagramSocket负责监听端口以及读写数据,DatagramPacket作为数据流对象进行传输。

UDP/IP是无连接的,无法进行双向通信,除非双方都成为UDP Server。

(4)UDP/IP+NIO

通过DatagramChannel和ByteBuffer实现。DatagramChannel负责端口监听及读写。ByteBuffer负责数据流传输。

如果要将消息发送到多台机器,如果为每个目标机器都建立一个连接的话,会有很大的网络流量压力。这时候可以使用基于UDP/IP的Multicast协议传输,Java中可以通过MulticastSocket和DatagramPacket来实现。

Multicast一般多用于多台机器的状态同步,比如JGroups。SRM, URGCP都是Multicast的实现方式。eBay就采用SRM来实现将数据从主数据库同步到各个搜索节点机器。

分享到:
评论

相关推荐

    详解Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)

    Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码) Java 网络IO编程是 Java 编程语言中最重要的知识点之一,涉及到网络编程的各种技术和模型。本篇文章主要介绍了 Java 网络IO编程总结,包括 BIO、NIO 和 AIO ...

    Java编程中的IO模型详解:BIO,NIO,AIO的区别与实际应用场景分析

    Java编程中的IO模型详解:BIO,NIO,AIO的区别与实际应用场景分析 IO模型在计算机编程中扮演着至关重要的角色,特别是在网络通信中。Java提供了三种主要的IO模型:BIO(Blocking IO),NIO(Non-blocking IO),...

    bio-nio-aio.zip

    《Java IO:从NIO到Reactor三种模式详解》 在Java编程中,IO操作是不可或缺的一部分,尤其在处理大量数据传输或者网络通信时。本文将深入探讨Java中的三种IO模型:传统IO(BIO)、非阻塞IO(NIO)以及反应器模式...

    Java BIO、NIO、AIO、Netty知识详解(值得珍藏)

    Java中的I/O(输入/输出)机制是程序与...在Java中,这五种模型分别对应BIO、NIO、AIO、传统的异步I/O(如Java的Future)以及基于回调的异步I/O。这些模型的选择取决于应用场景的需求,如连接数、操作类型和性能要求。

    2024年Java常见的-BIO,NIO,AIO,Netty面试题

    #### 二、BIO、NIO、AIO详解 3. **BIO(Blocking I/O)**: - **定义**:BIO是一种同步并阻塞的I/O模型,即在处理I/O请求时,服务器端需要为每个连接分配一个独立的线程进行处理,直到处理完成前该线程将一直处于...

    Java中网络IO的实现方式(BIO、NIO、AIO)介绍

    "Java中网络IO的实现方式(BIO、NIO、AIO)介绍" Java中网络IO的实现方式是指在Java语言中实现网络输入/输出操作的方法。根据不同的实现方式,可以分为三种:BIO(Blocking I/O)、NIO(Non-Blocking I/O)和AIO...

    java基础之IO流

    通过对比BIO、NIO和AIO的不同特点,可以根据实际应用场景选择最适合的技术方案。此外,深入理解Buffer、Channel和Selector的工作原理,有助于更好地利用Java NIO的强大功能,构建高性能的应用系统。

    Java三种IO模型原理实例详解

    Java中的IO模型可以分为三种:BIO(同步阻塞)、NIO(同步非阻塞)和AIO(异步非阻塞)。每种模型都有其特点和应用场景。 BIO(同步阻塞) BIO是最古老的IO模型,在JDK1.4之前都是使用BIO模式。在BIO模式下,...

    网络通信 netty_io

    【网络通信:Netty、BIO、NIO与AIO详解】 在计算机科学中,网络通信是实现系统间数据交换的关键技术。Java平台提供了多种I/O模型来支持网络通信,包括传统的阻塞I/O(BIO)、非阻塞I/O(NIO)以及异步I/O(AIO)。...

    IO模型介绍,理解java常见的IO模型

    为了应对这一挑战,Java提供了多种IO模型,包括BIO(Blocking IO)、NIO(Non-blocking IO)和AIO(Asynchronous IO)。本文将详细介绍这些模型的特点及其应用场景。 #### 二、BIO(Blocking IO) ##### 2.1 定义...

    Java 基础核心总结 +经典算法大全.rar

    BIO NIO 和 AIO 的区别什么是流 流的分类 节点流和处理流 Java IO 的核心类 File Java IO 流对象 字节流对象InputStream OutputStream 字符流对象Reader Writer 字节流与字符流的转换新潮的 NIO 缓冲区(Buffer)通道...

    java成神之路

    ### Java成神之路:核心知识点详解 #### 一、JVM与内存管理 **1. JVM内存结构** - **堆**: 所有线程共享的主要内存区域,用于存放对象实例和数组。 - **栈**: 每个线程在创建时都会创建一个栈空间,用于存放局部...

    java网络编程高清pdf

    综上所述,Java网络编程技术经历了从BIO到NIO再到AIO的发展过程,每一代技术都在解决特定场景下的性能问题。随着互联网技术的不断发展,高性能网络编程的需求日益增长,Java网络编程技术也在不断创新和发展,以适应...

    搜狐&美团旅行Java面试题.zip

    7. **IO/NIO/BIO**:了解I/O流的分类,熟悉FileInputStream、FileOutputStream等常用类的使用,理解NIO(New IO)与BIO( Blocking IO)的区别,以及AIO(Asynchronous IO)的相关概念。 8. **反射**:理解反射的...

    1、丰巢科技(51问)1

    本文档主要介绍了Java中的不同I/O模型,强调了BIO、NIO和AIO在处理并发连接时的效率差异,以及如何通过IO多路复用技术如select和epoll来提高系统性能。同时,也对ZooKeeper进行了简要介绍,展示了它在分布式系统中的...

    Java岗面试核心MCA版

    - BIO(Blocking IO)是传统的同步阻塞I/O模型,NIO(New IO)支持基于通道和缓冲区的非阻塞I/O操作,AIO(Asynchronous IO)是异步非阻塞I/O模型。 - **Files的常用方法**:`Files`类提供了一系列静态方法用于文件...

    Java思维导图xmind文件+导出图片

    IO 的基本概念、NIO、AIO、BIO深入分析 NIO的核心设计思想 Netty产生的背景及应用场景分析 基于Netty实现的高性能IM聊天 基于Netty实现Dubbo多协议通信支持 Netty无锁化串行设计及高并发处理机制 手写实现多...

    2018年最全Java面试通关秘籍第四套

    - **BIO、NIO、AIO**:BIO是阻塞I/O,NIO是非阻塞,AIO(或NIO.2)支持异步I/O。 - **长连接与短连接**:长连接保持连接状态,短连接每次请求建立新连接。 以上知识点是Java面试的常见问题,理解并掌握它们对于...

Global site tag (gtag.js) - Google Analytics