加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 建站 > 正文

架构师知识储备——深入理解BIO、NIO、AIO

发布时间:2019-10-18 17:18:54 所属栏目:建站 来源:咔咔侃技术
导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别;BIO、NIO、AIO 的区别;理解和实现 NIO 操作 Socket 时的多路复用;同时掌握 IO 最底层最核心的操作技巧。 BIO、NIO、AIO 的区别是什么? 同/异步、阻/非阻塞的区别是什么? 文件读写最优雅的实现方式是什么? N

在 Java 中,线程的实现是比较重量级的,所以线程的启动或者销毁是很消耗服务器的资源的,即使使用线程池来实现,使用上述传统的 Socket 方式,当连接数极具上升也会带来性能瓶颈,原因是线程的上线文切换开销会在高并发的时候体现的很明显,并且以上操作方式还是同步阻塞式的编程,性能问题在高并发的时候就会体现的尤为明显。

以上的流程,如下图:

架构师知识储备——深入理解BIO、NIO、AIO

4.2 NIO 多路复用

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

NIO 是利用了单线程轮询事件的机制,通过高效地定位就绪的 Channel,来决定做什么,仅仅 select 阶段是阻塞的,可以有效避免大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高。

  1. // NIO 多路复用 
  2. ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 4, 
  3.  60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 
  4. threadPool.execute(new Runnable() { 
  5.  @Override 
  6.  public void run() { 
  7.  try (Selector selector = Selector.open(); 
  8.  ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();) { 
  9.  serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); 
  10.  serverSocketChannel.configureBlocking(false); 
  11.  serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 
  12.  while (true) { 
  13.  selector.select(); // 阻塞等待就绪的Channel 
  14.  Set<SelectionKey> selectionKeys = selector.selectedKeys(); 
  15.  Iterator<SelectionKey> iterator = selectionKeys.iterator(); 
  16.  while (iterator.hasNext()) { 
  17.  SelectionKey key = iterator.next(); 
  18.  try (SocketChannel channel = ((ServerSocketChannel) key.channel()).accept()) { 
  19.  channel.write(Charset.defaultCharset().encode("你好,世界")); 
  20.  } 
  21.  iterator.remove(); 
  22.  } 
  23.  } 
  24.  } catch (IOException e) { 
  25.  e.printStackTrace(); 
  26.  } 
  27.  } 
  28. }); 
  29. // Socket 客户端(接收信息并打印) 
  30. try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) { 
  31.  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream())); 
  32.  bufferedReader.lines().forEach(s -> System.out.println("NIO 客户端:" + s)); 
  33. } catch (IOException e) { 
  34.  e.printStackTrace(); 
  • 首先,通过 Selector.open() 创建一个 Selector,作为类似调度员的角色;
  • 然后,创建一个 ServerSocketChannel,并且向 Selector 注册,通过指定 SelectionKey.OP_ACCEPT,告诉调度员,它关注的是新的连接请求;
  • 为什么我们要明确配置非阻塞模式呢?这是因为阻塞模式下,注册操作是不允许的,会抛出 IllegalBlockingModeException 异常;
  • Selector 阻塞在 select 操作,当有 Channel 发生接入请求,就会被唤醒;

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

架构师知识储备——深入理解BIO、NIO、AIO

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

4.3 AIO 版 Socket 实现

Java 1.7 提供了 AIO 实现的 Socket 是这样的,如下代码:

  1. // AIO线程复用版 
  2. Thread sThread = new Thread(new Runnable() { 
  3.  @Override 
  4.  public void run() { 
  5.  AsynchronousChannelGroup group = null; 
  6.  try { 
  7.  group = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(4)); 
  8.  AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); 
  9.  server.accept(null, new CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel>() { 
  10.  @Override 
  11.  public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel attachment) { 
  12.  server.accept(null, this); // 接收下一个请求 
  13.  try { 
  14.  Future<Integer> f = result.write(Charset.defaultCharset().encode("你好,世界")); 
  15.  f.get(); 
  16.  System.out.println("服务端发送时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); 
  17.  result.close(); 
  18.  } catch (InterruptedException | ExecutionException | IOException e) { 
  19.  e.printStackTrace(); 
  20.  } 
  21.  } 
  22.  @Override 
  23.  public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) { 
  24.  } 
  25.  }); 
  26.  group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); 
  27.  } catch (IOException | InterruptedException e) { 
  28.  e.printStackTrace(); 
  29.  } 
  30.  } 
  31. }); 
  32. sThread.start(); 
  33. // Socket 客户端 
  34. AsynchronousSocketChannel client = AsynchronousSocketChannel.open(); 
  35. Future<Void> future = client.connect(new InetSocketAddress(InetAddress.getLocalHost(), port)); 
  36. future.get(); 
  37. ByteBuffer buffer = ByteBuffer.allocate(100); 
  38. client.read(buffer, null, new CompletionHandler<Integer, Void>() { 
  39.  @Override 
  40.  public void completed(Integer result, Void attachment) { 
  41.  System.out.println("客户端打印:" + new String(buffer.array())); 
  42.  } 
  43.  @Override 
  44.  public void failed(Throwable exc, Void attachment) { 
  45.  exc.printStackTrace(); 
  46.  try { 
  47.  client.close(); 
  48.  } catch (IOException e) { 
  49.  e.printStackTrace(); 
  50.  } 
  51.  } 
  52. }); 
  53. Thread.sleep(10 * 1000); 

五、总结

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读