详解 同步异步阻塞非阻塞 与 BIO NIO AIO区别多路复用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解 同步异步阻塞非阻塞 与 BIO NIO AIO区别多路复用相关的知识,希望对你有一定的参考价值。

(目录)


一、IO 介绍

IO的全称其实是:Input/Output的缩写

传统的 IO 大致可以分为 4种类型


那内核是如何进行IO交互的呢?

对应抽象到java的socket代码简单示例如下:

public class SocketServer 
  public static void main(String[] args) throws Exception 
    // 监听指定的端口
    int port = 8080;
    ServerSocket server = new ServerSocket(port);
    // server将一直等待连接的到来
    Socket socket = server.accept();
    // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
    InputStream inputStream = socket.getInputStream();
    byte[] bytes = new byte[1024];
    int len;
    while ((len = inputStream.read(bytes)) != -1) 
      //获取数据进行处理
      String message = new String(bytes, 0, len,"UTF-8");
    
    // 省略……
  


二、同步与异步

同步 和 异步 指的是:

假设我们的执行流程中:依次是方法一和方法二

同步与异步是从多个线程之间的协调来实现效率差异


三、阻塞与非阻塞

阻塞与非阻塞 主要是从 CPU 的消耗上来说

虽然表面上看 非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加增加的 CPU 使用时间能不能补偿系统的切换成本需要好好评估

阻塞与非阻塞关注的是线程是否在原地等待


四、BIO NIO AIO

1. BIO


2. NIO

NIO也叫Non-Blocking IO 是同步非阻塞的IO模型

Java中的NIO 是new IO的意思。其实是NIO加上IO多路复用技术普通的NIO是线程轮询查看一个IO缓冲区是否就绪,而Java中的new IO指的是线程轮询地去查看一堆IO缓冲区中哪些就绪,这是一种IO多路复用的思想。IO多路复用模型中,将检查IO数据是否就绪的任务,交给系统级别的select或epoll模型,由系统进行监控,减轻用户线程负担。


3. AIO

AIO是真正意义上的异步非阻塞IO模型

AIO可以做到真正的异步的操作,但实现起来比较复杂,支持纯异步IO的操作系统非常少,目前也就windows是IOCP技术实现了,而在Linux上,底层还是是使用的epoll实现的。


总结:

举个例子:

哪种方式更有效率呢?是不是一目了然呢?



五、Socket 和 NIO 的多路复用

1. 传统的 Socket 实现

代码如下:

int port = 4343; //端口号
// Socket 服务器端(简单的发送信息)
Thread sThread = new Thread(new Runnable() 
    @Override
    public void run() 
        try 
            ServerSocket serverSocket = new ServerSocket(port);
            while (true) 
                // 等待连接
                Socket socket = serverSocket.accept();
                Thread sHandlerThread = new Thread(new Runnable() 
                    @Override
                    public void run() 
                        try (PrintWriter printWriter = new PrintWriter(socket.getOutputStream())) 
                            printWriter.println("hello world!");
                            printWriter.flush();
                         catch (IOException e) 
                            e.printStackTrace();
                        
                    
                );
                sHandlerThread.start();
            
         catch (IOException e) 
            e.printStackTrace();
        
    
);
sThread.start();

// Socket 客户端(接收信息并打印)
try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) 
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream()));
    bufferedReader.lines().forEach(s -> System.out.println("客户端:" + s));
 catch (UnknownHostException e) 
    e.printStackTrace();
 catch (IOException e) 
    e.printStackTrace();

  • 调用 accept 方法,阻塞等待客户端连接;
  • 利用 Socket 模拟了一个简单的客户端,只进行连接、读取和打印;

以上的流程,如下图:


2. NIO 多路复用

介于以上高并发的问题,NIO 的多路复用功能就显得意义非凡了

// NIO 多路复用
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 4,
        60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
threadPool.execute(new Runnable() 
    @Override
    public void run() 
        try (Selector selector = Selector.open();
             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();) 
            serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) 
                selector.select(); // 阻塞等待就绪的Channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) 
                    SelectionKey key = iterator.next();
                    try (SocketChannel channel = ((ServerSocketChannel) key.channel()).accept()) 
                        channel.write(Charset.defaultCharset().encode("你好,世界"));
                    
                    iterator.remove();
                
            
         catch (IOException e) 
            e.printStackTrace();
        
    
);

// Socket 客户端(接收信息并打印)
try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) 
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream()));
    bufferedReader.lines().forEach(s -> System.out.println("NIO 客户端:" + s));
 catch (IOException e) 
    e.printStackTrace();

下面的图,可以有效的说明 NIO 复用的流程:

就这样 NIO 的多路复用就大大提升了服务器端响应高并发的能力。


以上是关于详解 同步异步阻塞非阻塞 与 BIO NIO AIO区别多路复用的主要内容,如果未能解决你的问题,请参考以下文章

BIO,NIO,AIO详解

同步,异步,阻塞,非阻塞,bio,nio,aio

(转)IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别

IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别

IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别

浅谈NIO