千家信息网

Java的BIO, NIO, AIO怎么实现

发表于:2024-11-16 作者:千家信息网编辑
千家信息网最后更新 2024年11月16日,本篇内容主要讲解"Java的BIO, NIO, AIO怎么实现",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Java的BIO, NIO, AIO怎么实现"
千家信息网最后更新 2024年11月16日Java的BIO, NIO, AIO怎么实现

本篇内容主要讲解"Java的BIO, NIO, AIO怎么实现",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Java的BIO, NIO, AIO怎么实现"吧!

从编程语言层面

BIO, NIO, AIO以Java的角度理解:

  • BIO,同步阻塞式IO,简单理解:一个连接一个线程

  • NIO,同步非阻塞IO,简单理解:一个请求一个线程

  • AIO,异步非阻塞IO,简单理解:一个有效请求一个线程

BIO

在JDK1.4之前,用Java编写网络请求,都是建立一个ServerSocket,然后,客户端建立Socket时就会询问是否有线程可以处理,如果没有,要么等待,要么被拒绝。即:一个连接,要求Server对应一个处理线程。

public class PlainEchoServer {  public void serve(int port) throws IOException {    final ServerSocket socket = new ServerSocket(port); //Bind server to port    try {      while (true) {        //Block until new client connection is accepted        final Socket clientSocket = socket.accept();        System.out.println("Accepted connection from " + clientSocket);        //Create new thread to handle client connection        new Thread(new Runnable() {          @Override          public void run() {            try {              BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));              PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);              //Read data from client and write it back              while (true) {                writer.println(reader.readLine());                writer.flush();              }            } catch (IOException e) {              e.printStackTrace();              try {                clientSocket.close();              } catch (IOException ex) {                // ignore on close              }            }          }        }).start();        //Start thread      }    } catch (IOException e) {      e.printStackTrace();    }  }}

NIO

在Java里的由来,在JDK1.4及以后版本中提供了一套API来专门操作非阻塞I/O,我们可以在java.nio包及其子包中找到相关的类和接口。由于这套API是JDK新提供的I/O API,因此,也叫New I/O,这就是包名nio的由来。这套API由三个主要的部分组成:缓冲区(Buffers)、通道(Channels)和非阻塞I/O的核心类组成。在理解NIO的时候,需要区分,说的是New I/O还是非阻塞IO,New I/O是Java的包,NIO是非阻塞IO概念。这里讲的是后面一种。

NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题:在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。 NIO基于Selector,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

public class PlainNioEchoServer {  public void serve(int port) throws IOException {    System.out.println("Listening for connections on port " + port);    ServerSocketChannel serverChannel = ServerSocketChannel.open();    ServerSocket ss = serverChannel.socket();    InetSocketAddress address = new InetSocketAddress(port);    //Bind server to port    ss.bind(address);    serverChannel.configureBlocking(false);    Selector selector = Selector.open();    //Register the channel with the selector to be interested in new Client connections that get accepted    serverChannel.register(selector, SelectionKey.OP_ACCEPT);    while (true) {      try {        //Block until something is selected        selector.select();      } catch (IOException ex) {        ex.printStackTrace();        //handle in a proper way        break;      }      //Get all SelectedKey instances      Set readyKeys = selector.selectedKeys();      Iterator iterator = readyKeys.iterator();      while (iterator.hasNext()) {        SelectionKey key = (SelectionKey) iterator.next();        //Remove the SelectedKey from the iterator        iterator.remove();        try {          if (key.isAcceptable()) {            ServerSocketChannel server = (ServerSocketChannel) key.channel();            //Accept the client connection            SocketChannel client = server.accept();            System.out.println("Accepted connection from " + client);            client.configureBlocking(false);            //Register connection to selector and set ByteBuffer            client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, ByteBuffer.allocate(100));          }          //Check for SelectedKey for read          if (key.isReadable()) {            SocketChannel client = (SocketChannel) key.channel();            ByteBuffer output = (ByteBuffer) key.attachment();            //Read data to ByteBuffer            client.read(output);          }          //Check for SelectedKey for write          if (key.isWritable()) {            SocketChannel client = (SocketChannel) key.channel();            ByteBuffer output = (ByteBuffer) key.attachment();            output.flip();            //Write data from ByteBuffer to channel            client.write(output);            output.compact();          }        } catch (IOException ex) {          key.cancel();          try {            key.channel().close();          } catch (IOException cex) {          }        }      }    }  }}

AIO

与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。

即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。

在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:

  • AsynchronousSocketChannel

  • AsynchronousServerSocketChannel

  • AsynchronousFileChannel

  • AsynchronousDatagramChannel

其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。

public class PlainNio2EchoServer {  public void serve(int port) throws IOException {    System.out.println("Listening for connections on port " + port);    final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();    InetSocketAddress address = new InetSocketAddress(port);    // Bind Server to port    serverChannel.bind(address);    final CountDownLatch latch = new CountDownLatch(1);    // Start to accept new Client connections. Once one is accepted the CompletionHandler will get called.    serverChannel.accept(null, new CompletionHandler() {      @Override      public void completed(final AsynchronousSocketChannel channel, Object attachment) {        // Again accept new Client connections        serverChannel.accept(null, this);        ByteBuffer buffer = ByteBuffer.allocate(100);        // Trigger a read operation on the Channel, the given CompletionHandler will be notified once something was read        channel.read(buffer, buffer, new EchoCompletionHandler(channel));      }      @Override      public void failed(Throwable throwable, Object attachment) {        try {          // Close the socket on error          serverChannel.close();        } catch (IOException e) {          // ingnore on close        } finally {          latch.countDown();        }      }    });    try {      latch.await();    } catch (InterruptedException e) {      Thread.currentThread().interrupt();    }  }  private final class EchoCompletionHandler implements CompletionHandler {    private final AsynchronousSocketChannel channel;    EchoCompletionHandler(AsynchronousSocketChannel channel) {      this.channel = channel;    }    @Override    public void completed(Integer result, ByteBuffer buffer) {      buffer.flip();      // Trigger a write operation on the Channel, the given CompletionHandler will be notified once something was written      channel.write(buffer, buffer, new CompletionHandler() {        @Override        public void completed(Integer result, ByteBuffer buffer) {          if (buffer.hasRemaining()) {            // Trigger again a write operation if something is left in the ByteBuffer            channel.write(buffer, buffer, this);          } else {            buffer.compact();            // Trigger a read operation on the Channel, the given CompletionHandler will be notified once something was read            channel.read(buffer, buffer, EchoCompletionHandler.this);          }        }        @Override        public void failed(Throwable exc, ByteBuffer attachment) {          try {            channel.close();          } catch (IOException e) {            // ingnore on close          }        }      });    }    @Override    public void failed(Throwable exc, ByteBuffer attachment) {      try {        channel.close();      } catch (IOException e) {        // ingnore on close      }    }  }}

实现原理

说道实现原理,还要从操作系统的IO模型上了解 按照《Unix网络编程》的划分,IO模型可以分为:阻塞IO、非阻塞IO、IO复用、信号驱动IO和异步IO,按照POSIX标准来划分只分为两类:同步IO和异步IO。 如何区分呢?首先一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作,同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO复用、信号驱动IO都是同步IO,如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO。阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

收到操作系统的IO模型,又不得不提select/poll/epoll/iocp。 可以理解的说明是:在Linux 2.6以后,java NIO的实现,是通过epoll来实现的,这点可以通过jdk的源代码发现。而AIO,在windows上是通过IOCP实现的,在linux上还是通过epoll来实现的。 这里强调一点:AIO,这是I/O处理模式,而epoll等都是实现AIO的一种编程模型;换句话说,AIO是一种接口标准,各家操作系统可以实现也可以不实现。在不同操作系统上在高并发情况下最好都采用操作系统推荐的方式。Linux上还没有真正实现网络方式的AIO。

底层基础

在windows上,AIO的实现是通过IOCP来完成的,看JDK的源代码,可以发现

WindowsAsynchronousSocketChannelImpl

看实现接口:

implements Iocp.OverlappedChannel

再看实现方法:里面的read0/write0方法是native方法,调用的jvm底层实现。

在linux上,AIO的实现是通过epoll来完成的,看JDK源码,可以发现,实现源码是:

UnixAsynchronousSocketChannelImpl

看实现接口:

implements Port.PollableChannel

这是与windows最大的区别,poll的实现,在linux2.6后,默认使用epoll。

到此,相信大家对"Java的BIO, NIO, AIO怎么实现"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阻塞 线程 操作系统 系统 方法 处理 同步 客户 就是 客户端 接口 模型 程序 网络 应用 内容 函数 同时 实际 缓冲区 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 服务器不能访问某一个域名 卫健局网络安全风险评估报告 广西联客网络技术 移动应用开发和软件开发区别 数据库中的数据冗余多 网络安全比例 metadata数据库视图 云主机服务器哪个厂家质量好 天津服务器回收选哪家云主机 江苏华勤应用软件开发 软件开发综合实践课程目标 网络安全检测和处置 南京移动软件开发大会 dayz服务器管理员怎么传送 吉林电信dns服务器虚拟主机 网络安全哪个部门监管 java显示文件服务器下的图片 阿里云网站服务器多少钱一个月 开展手机网络安全保密教育 安易财务软件的数据库 互联网金融网络安全规定 电信网络安全协会 大学生在线 网络安全 服务器管理盘安装教程 网络安全文明辩论赛利与弊 河北统一软件开发服务参考价格 npf服务器安装失败 access数据库下载 我的世界新手服务器 网络安全研究都有哪些方面
0