快捷搜索:  汽车  科技

阻塞io和非阻塞io的区别:阻塞 非阻塞 多路复用

阻塞io和非阻塞io的区别:阻塞 非阻塞 多路复用同步非阻塞IO之NIO:服务器端保存一个Socket连接列表,然后对这个列表进行轮询,如果发现某个Socket端口上有数据可读时说明读就绪,则调用该socket连接的相应读操作。如果发现某个 Socket端口上有数据可写时说明写就绪,则调用该socket连接的相应写操作。如果某个端口的Socket连接已经中断,则调用相应的析构方法关闭该端口。这样能充分利用服务器资源,效率得到了很大提高,在进行IO操作请求时候再用个线程去处理,是一个请求一个线程。Java中使用Selector、Channel、Buffer来实现上述效果。// 开启服务监听线程 当收到连接请求后 开启新的线程进行处理 public class ServerThread implements Runnable{ @Override public void run() { try {

BIO特点

  1. 使用一个独立的线程维护一个socket连接,随着连接数量的增多,对虚拟机造成一定压力。
  2. 使用流来读取数据,流是阻塞的,当没有可读/可写数据时,线程等待,会造成资源的浪费。
3.1.1 BIO 样例

常量:

public class Constant { public static final String HOST = "127.0.0.1"; public static final int PORT = 8080; }

主类:

public class ClientMain { public static void main(String[] args) { //开启服务 System.out.println("开启服务 监听端口:" Constant.PORT); new Thread(new ServerThread()).start(); //建立一个socket客户端 发起请求 System.out.println("客户端 请求连接 并发送数据"); try { Socket socket = new Socket(Constant.HOST Constant.PORT); //开启新的线程处理socket连接 new Thread(new ClientProcessThread(socket)).start(); } catch (IOException e) { e.printStackTrace(); } } }

服务端监听线程:

// 开启服务监听线程 当收到连接请求后 开启新的线程进行处理 public class ServerThread implements Runnable{ @Override public void run() { try { ServerSocket serverSocket = new ServerSocket(Constant.PORT); while (true){ Socket socket = serverSocket.accept(); new Thread(new ServerProcessThread(socket)).start(); //开启新的线程进行连接请求的处理 } } catch (IOException e) { e.printStackTrace(); } } }

服务端处理线程:

import java.io.*; import java.net.Socket; /** * 服务端收到连接请求后 处理请求的线程 阻塞式IO */ public class ServerProcessThread implements Runnable { private Socket socket; public ServerProcessThread(Socket socket){ this.socket = socket; } @Override public void run() { //获取客户端的数据 并写回 //等待响应 try { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line = ""; String requestStr = ""; System.out.println("来自客户端的数据:"); // 读取客户端数据 while((line = bufferedReader.readLine()) != null){ requestStr = line; System.out.println(line); } // 从服务端发给客户端数据 Writer writer = new OutputStreamWriter(socket.getOutputStream()); writer.write("data from server " requestStr "\r\n"); writer.flush(); writer.close(); bufferedReader.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }

客户端:

/** * 维护客户端socket连接的线程 阻塞式IO */ public class ClientProcessThread implements Runnable { private Socket socket; public ClientProcessThread(Socket socket){ this.socket = socket; } @Override public void run() { //写数据 等待响应 输出响应 String requestStr = "data from client \r\n"; try { Writer writer = new OutputStreamWriter(socket.getOutputStream()); writer.write(requestStr); writer.flush(); socket.shutdownOutput(); //等待响应 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; System.out.println("来自服务端的响应:"); while((line = bufferedReader.readLine()) != null){ System.out.println(line); } writer.close(); bufferedReader.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }

阻塞io和非阻塞io的区别:阻塞 非阻塞 多路复用(1)

3.2 NIO

同步非阻塞IO之NIO:服务器端保存一个Socket连接列表,然后对这个列表进行轮询,如果发现某个Socket端口上有数据可读时说明读就绪,则调用该socket连接的相应读操作。如果发现某个 Socket端口上有数据可写时说明写就绪,则调用该socket连接的相应写操作。如果某个端口的Socket连接已经中断,则调用相应的析构方法关闭该端口。这样能充分利用服务器资源,效率得到了很大提高,在进行IO操作请求时候再用个线程去处理,是一个请求一个线程。Java中使用Selector、Channel、Buffer来实现上述效果。

阻塞io和非阻塞io的区别:阻塞 非阻塞 多路复用(2)

每个线程中包含一个Selector对象,它相当于一个通道管理器,可以实现在一个线程中处理多个通道的目的,减少线程的创建数量。远程连接对应一个channel,数据的读写通过buffer均在同一个channel中完成,并且数据的读写是非阻塞的。通道创建后需要注册在selector中,同时需要为该通道注册感兴趣事件(客户端连接服务端事件、服务端接收客户端连接事件、读事件、写事件),selector线程需要采用轮训的方式调用selector的select函数,直到所有注册通道中有兴趣的事件发生,则返回,否则一直阻塞。而后循环处理所有就绪的感兴趣事件。以上步骤解决BIO的两个瓶颈:

  1. 不必对每个连接分别创建线程。
  2. 数据读写非阻塞。

下面对以下三个概念做一个简单介绍,Java NIO由以下三个核心部分组成:

  1. selector:Selector 允许单线程处理多个Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。要使用Selector,得向Selector注册Channel,然后调用他的select方法,这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子入有新连接接进来,数据接收等。
  2. Channel:基本上所有的IO在NIO中都从一个Channel开始。Channel有点像流,数据可以从channel到buffer,也可以从buffer到channel。
  3. Buffer:缓冲区本质上是一个可以读写数据的内存块,可以理解成是一个容器对象(含数组),该对象提供了一组方法,可以更轻松的使用内存块,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变换情况,Channel提供从文件,网络读取数据的渠道,但是读取或者写入的数据都必须经由Buffer。

channel和buffer有好几种类型。下面是Java NIO中的一些主要channel的实现:

FileChannel DatagramChannel SocketChannel ServerSocketChannel

正如你所看到的,这些通道涵盖了UDP和TCP网络IO,以及文件IO。以下是Java NIO里关键的buffer实现:

ByteBuffer CharBuffer FloatBuffer IntBuffer LongBuffer ShortBuffer

在微服务阶段,一个请求可能涉及到多个不同服务之间的跨服务器调用,如果你想实现高性能的PRC框架来进行数据传输,那就可以基于Java NIO做个支持长连接、自定义协议、高并发的框架,比如Netty。Netty本身就是一个基于NIO的网络框架, 封装了Java NIO那些复杂的底层细节,给你提供简单好用的抽象概念来编程。比如Dubbo底层就是用的Netty。

阻塞io和非阻塞io的区别:阻塞 非阻塞 多路复用(3)

3.3 AIO

AIO是异步非阻塞IO,相比NIO更进一步,进程读取数据时只负责发送跟接收指令,数据的准备工作完全由操作系统来处理。

参考资料

阻塞io和非阻塞io的区别:阻塞 非阻塞 多路复用(4)

推荐一个零声教育C/C 后台开发的免费公开课程,个人觉得老师讲得不错,分享给大家:C/C 后台开发高级架构师,内容包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,C/C Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂 立即学习

原文:阻塞、非阻塞、多路复用、同步、异步、BIO、NIO、AIO 一锅端

猜您喜欢: