做门窗安装用哪些网站找生意,wordpress树莓派,wordpress 返回首页,怎么做网站海报轮播图【JAVA基础】- 同步非阻塞模式NIO详解 文章目录 【JAVA基础】- 同步非阻塞模式NIO详解一、概述二、常用概念三、NIO的实现原理四、NIO代码实现客户端实现服务端实现 五、同步非阻塞NIO总结 一、概述
NIO#xff08;Non-Blocking IO#xff09;是同步非阻塞方式来处理IO数据。…【JAVA基础】- 同步非阻塞模式NIO详解 文章目录 【JAVA基础】- 同步非阻塞模式NIO详解一、概述二、常用概念三、NIO的实现原理四、NIO代码实现客户端实现服务端实现 五、同步非阻塞NIO总结 一、概述
NIONon-Blocking IO是同步非阻塞方式来处理IO数据。服务器实现模式为一个请求一个线程即客户端发送的链接请求都会注册到选择器上选择器轮询到连接有IO请求时才启动一个线程进行处理。
二、常用概念
同步(synchronous)调用方式指应用Application,调用方发起有一个功能调用时在没有得到功能的结果之前该调用不会返回。也就是说调用方会一直等待被调用方返回功能的结果。异步(asynchronous)调用方发起一个功能调用时没有得到功能的结果立即返回后续被调用方再通过回调等手段把功能的结构通知调用方。也就是调用方立即得到返回但是返回中不包含执行的结果。
同步和异步强调的是消息通信机制 (synchronous communication/ asynchronous communication)。所谓同步就是在发出一个调用时在没有得到结果之前该“调用”就不返回。但是一旦调用返回就得到返回值了。换句话说就是由“调用者”主动等待这个“调用”的结果。而异步则是相反调用在发出之后这个调用就直接返回了所以没有返回结果。换句话说当一个异步过程调用发出后调用者不会立刻得到结果。而是在调用发出后被调用者通过状态、通知来通知调用者或通过回调函数处理这个调用
阻塞线程发起一个调用时, 在调用返回之前, 线程会被阻塞, 在这个状态下会交出当前CPU的使用权而暂停也就是调用方会等待调用结果, 调用阻塞了调用方的线程, 线程不在运行处理中。非阻塞线程发起一个调用时, 调用会立即返回, 避免线程被阻塞。但是, 返回的结果只是被调用方当前状态的值, 实际使用时, 调用方需要轮询, 直到返回结果符合预期(直到数据准备好)。
阻塞和非阻塞 强调的是程序在等待调用结果消息返回值时的状态. 阻塞调用是指调用结果返回之前当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前该调用不会阻塞当前线程。 对于同步调用来说很多时候当前线程还是激活的状态只是从逻辑上当前函数没有返回而已即同步等待时什么都不干白白占用着资源。
同步阻塞 IO[BIO - BlockingIO]在此种方式下用户进程在发起一个 IO 操作以后必须等待 IO 操作的完成只有当真正完成了 IO 操作以后用户进程才能运行。 JAVA传统的 IO 模型属于此种方式。同步非阻塞 IO[Non-Blocking IO]在此种方式下用户进程发起一个 IO 操作以后 边可 返回做其它事情但是用户进程需要时不时的询问 IO 操作是否就绪这就要求用户进程不停的去询问从而引入不必要的 CPU 资源浪费。其中目前 JAVA 的 NIO 就属于同步非阻塞 IO 。异步阻塞 IO[IO Multiplexing]此种方式下是指应用发起一个 IO 操作以后不等待内核 IO 操作的完成等内核完成 IO 操作以后会通知应用程序这其实就是同步和异步最关键的区别同步必须等待或者主动的去询问 IO 是否完成那么为什么说是阻塞的呢因为此时是通过 select 系统调用来完成的而 select 函数本身的实现方式是阻塞的而采用 select 函数有个好处就是它可以同时监听多个文件句柄从而提高系统的并发性异步非阻塞 IO[Asynchronous IO]: 在此种模式下用户进程只需要发起一个 IO 操作然后立即返回等 IO 操作真正的完成以后应用程序会得到 IO 操作完成的通知此时用户进程只需要对数据进行处理就好了不需要进行实际的 IO 读写操作因为 真正的 IO读取或者写入操作已经由 内核完成了。目前 Java 中还没有支持此种 IO 模型。
三、NIO的实现原理 Java的NIO主要由三个核心部分组成Channel(通道)、Buffer(缓冲区)、Selector。
所有的IO在NIO中都从一个Channel开始数据可以从Channel读到Buffer中也可以从Buffer写到Channel中。Channel有好几种类型其中比较常用的有FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel等这些通道涵盖了UDP和TCP网络IO以及文件IO。
Buffer本质上是一块可以写入数据然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象并提供了一组方法用来方便的访问该块内存。Java NIO里关键的Buffer实现有CharBuffer、ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。这些Buffer覆盖了你能通过IO发送的基本数据类型即byte、short、int、long、float、double、char。
Buffer对象包含三个重要的属性分别是capacity、position、limit其中position和limit的含义取决于Buffer处在读模式还是写模式。但不管Buffer处在什么模式capacity的含义总是一样的。
capacity作为一个内存块Buffer有个固定的最大值就是capacity。Buffer只能写capacity个数据一旦Buffer满了需要将其清空才能继续写数据往里写数据。
position当写数据到Buffer中时position表示当前的位置。初始的position值为0。当一个数据写到Buffer后 position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity–1。当读取数据时也是从某个特定位置读。当将Buffer从写模式切换到读模式position会被重置为0。当从Buffer的position处读取数据时position向前移动到下一个可读的位置。
limit在写模式下Buffer的limit表示最多能往Buffer里写多少数据此时limit等于capacity。当切换Buffer到读模式时 limit表示你最多能读到多少数据此时limit会被设置成写模式下的position值。
Selector允许单线程处理多个 Channel如果你的应用打开了多个连接通道但每个连接的流量都很低使用Selector就会很方便。要使用Selector得向Selector注册Channel然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回线程就可以处理这些事件事件例如有新连接进来数据接收等。
四、NIO代码实现
客户端实现 步骤 创建SocketChannel 通道切换异步非阻塞模式configureBlocking(false)设置缓冲区大小ByteBuffer.allocate(1024)值写入缓冲区 buffer.put(input.getBytes())缓冲区中的值写入通道中channel.write() 代码演示 public static void main(String[] args) throws IOException {//创建通道SocketChannel channelSocketChannel.open(new InetSocketAddress(127.0.0.1,6001));//切换异步非阻塞模式channel.configureBlocking(false);//设置缓冲去大小ByteBuffer bufferByteBuffer.allocate(1024);System.out.println(输入传输值);//获取键盘输入的值Scanner scanner new Scanner(System.in);while (scanner.hasNext()){String inputscanner.next();//把获取的值写入缓冲区中buffer.put(input.getBytes());buffer.flip();//把缓冲区中的值写入通道中channel.write(buffer);buffer.clear();}channel.close();}服务端实现 步骤 创建ServerSocketChannel通道切换异步非阻塞模式configureBlocking(false)绑定连接获取选择器 Selector open Selector.open()将通道注册到选择器并指定监听接受事件轮训式获取选择已准备就绪的事件获取当前选择器所有注册的监听事件获取准备就绪的事件判断是什么事件准备就绪接受就绪获取客户端连接设置非阻塞异步模式将通道注册到服务器上 代码演示 public static void main(String[] args) throws IOException {//创建通道ServerSocketChannel channelServerSocketChannel.open();//切换到异步非阻塞模式channel.configureBlocking(false);//绑定链接channel.bind(new InetSocketAddress(6001));//获取选择器Selector open Selector.open();//将通道注册到选择器,并指定监听接受事件channel.register(open, SelectionKey.OP_ACCEPT);//轮训式获取选择已经准备就绪的事件while(open.select() 0) {//获取当前选择器所有注册的监听事件IteratorSelectionKey it open.selectedKeys().iterator();while(it.hasNext()) {//获取准备就绪的事件SelectionKey sk it.next();//判断是什么事件准备就绪if(sk.isAcceptable()) {//接受就绪获取客户端连接SocketChannel sc channel.accept();//设置非阻塞异步模式sc.configureBlocking(false);//将通道注册到服务器上sc.register(open, SelectionKey.OP_READ);} else if(sk.isReadable()) {//获取当前选择器就绪的通道SocketChannel s (SocketChannel) sk.channel();ByteBuffer bb ByteBuffer.allocate(1024);int len 0;while((len s.read(bb)) 0) {bb.flip();System.out.println(new String(bb.array(),0,len));bb.clear();}}}it.remove();}}五、同步非阻塞NIO总结
同步非阻塞的特点应用程序的线程需要不断的进行IO系统调用轮询数据是否已经准备好如果没有准备好就继续轮询直到完成IO系统调用为止。
同步非阻塞IO的特点每次发起的IO系统调用在内核等待数据过程中可以立即返回。用户线程不会被阻塞实时性较好。
同步非阻塞IO的缺点 不断地轮询内核这将占用大量的CPU时间效率低下。
总体来说在高并发应用场景下同步非阻塞IO也是不可用的。一般Web服务器不适用这种IO模型。这种IO模型一般很少直接使用而是在其他IO模型中使用非阻塞IO这一特性。