光伏电站建设的国家网站湖南网站托管

news/2025/10/2 22:53:53/文章来源:
光伏电站建设的国家网站,湖南网站托管,成都行业网站建设那里好,网站建设后台管理怎么管理java之NIO 1 什么是NIO Java NIO (New IO#xff0c;Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API。NIO支持面向缓冲区的、基于通道的IO操作。NIO的三大核心部分#xff1a;通道(Channel)#xff0c;缓冲区(Buffer), 选择器(Selector)#xff0c;数据总是从…                                                 java之NIO 1 什么是NIO Java NIO (New IONon-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API。NIO支持面向缓冲区的、基于通道的IO操作。NIO的三大核心部分通道(Channel)缓冲区(Buffer), 选择器(Selector)数据总是从通道读取到缓冲区或者从缓冲区写入到通道中选择器用于监听多个通道事件如连接打开数据到达等。Java NIO系统的核心在于通道(Channel)和缓冲区(Buffer)Channel负责传输Buffer负责存储数据。 BIO与NIO的理解传统IO即BIO在进行数据传输时必须要建立一个连接才能进行数据的写入和读取。可以吧数据理解为水流需要有管道可以认为应用程序和文件之间的连接就是一个管道用来运输水流。输入流和输出流是不同的管道他们是单向的。NIO在进程应用程序和文件之间数据传输时他们的连接不能理解为管道他有个概念为“通道”可以理解为铁轨还有“缓冲区”可以理解为火车。起到运输作用但是本事不能进行运输数据数据的运输需要借助于火车。当我们要读取磁盘文件的时候数据会先加载到缓冲区然后传输到应用程序。 2 BIO与NIO的区别 1BIO是面向流流是单向的。每次从流中读取一个或者多个字节直到读取完所有字节没有被缓存起来不能前后移动流中的数据如果想要能前后移动的话需要将他缓存到另外一个缓冲区NIO是面向缓冲区的通道可以将数据读取到缓存区实现双向传输。NIO是将数据读取到一个稍后处理的缓冲区并且在需要的时候可以前后移动。 2BIO是阻塞式一个线程调用read()或者write()的时候这个线程被阻塞直到数据被读取或者完全写入不能再干其他事情NIO是非阻塞式一个线程从一个通道发送请求读取数据只能获取到目前可用的没数据可用就什么都不会获取不保持阻塞直到数据变得可以读取之前这个线程可以做其他事写也是这样。非阻塞IO的线程在空闲时间作用在其他通道上执行IO操作那么一个线程就可以管理多个输入输出通道。 3BIO传输的是字节流或字符流NIO是通过块传输。 面向文件IO的区别BIO是面向流的NIO是面向缓冲区的。面向网络IO的区别BIO是阻塞的NIO是非阻塞的并且NIO有选择器 3 缓冲区Buffer 3.1 缓冲区相关概念 通道表示IO源到 IO 设备(例如文件、套接字)的连接。若需要使用 NIO 系统需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区对数据进行处理。 缓冲区(Buffer)Buffer主要用于与Channel交互数据从Channel写入Buffer然后再从Buffer写出到Channel。是一个用于特定基本数据类型(除boolean型外)的容器底层使用数组存储可以保存多个相同类型的数据。所有缓冲区都是java.nio.buffer的子类常见的子类有ByteBuffer,CharBuffer,IntBuffer,DoubleBuffer,ShortBuffer,LongBuffer,FloatBuffer等他们管理数据的方法都相似管理的类型不同而已。 Buffer的实现类都是通过allocate(int,capacity)创建一个容量为capacity的对象。Buffer有以下基本属性 容量capacity标识Buffer存储的最大数据容量声明后不能更改不能为负通过capacity()获取限制limit第一个不应该读取或写入的数据的索引也就是limit后的数据不可以读写不能为负不能大于capacity通过limit()获取位置position当前要读取或者写入数据的索引不能为负不能大于limit通过position()获取标记mark标记是一个索引通过mark()标记后可以通过调用reset()将position恢复到标记的mark处 上述属性的范围大小为 0 mark position limit capacity 3.2 缓冲区的基本操作 缓冲区为所有的子类提供了两个用于数据操作的方法put和get方法如ByteBuffer的这两个方法如下 方法说明put(byte b)将指定的单个字节写入缓冲区的当前位置put(byte[] buf)将buf中的字节写入缓冲区的当前位置put(int index,byte b)将指定字节写入缓冲区的索引位置不移动positionget()读取单个字节get(byte[] buf)批量读取多个字节到buf中get(int index)读取指定索引位置的字节不移动position Buffer其他常用方法 方法说明Buffer flip()将limit设置为当前positionposition设置为0mark设置为-1Buffer rewind()将position设置为0mark设置为-1可以重复读Buffer clear()将limit设置为capacityposition设置为0mark设置为-1数据没有清空Buffer mark()设置缓冲区的markBuffer reset()将当前位置的position转到之前设置的mark的位置Buffer hasRemaining()判断缓冲区中是否还有元素int remaining返回position和limit之间元素的个数Xxx[] array()返回XxxBuffer底层的Xxx数组int capacity()返回Buffer的capacity大小int limit()返回Buffer的limit位置Buffer limit(int n)将设置缓冲区界限为 n, 并返回一个具有新 limit 的缓冲区对象int position()返回Buffer的position位置Buffer position(int n)将设置缓冲区的当前位置为 n , 并返回修改后的 Buffer 对象说明①当我们调用ByteBuffer.allocate(10)方法创建了一个10个byte的数组的缓冲区position的位置为0capacity和limit默认都是数组长度。②当通过put方法写入5个字节到缓冲区时position更新为5。③需要将缓冲区中的5个字节数据写入Channel的通信信道调用ByteBuffer.flip()方法变化为position设回0并将limit设成之前的position的值④这时底层操作系统就可以从缓冲区中正确读取这个5个字节数据并发送出去了。在下一次写数据之前我们再调用clear()方法缓冲区的索引位置又回到了初始位置。 注意clear()是把position设回0limit设置成capacity换句话说其实Buffer中的数据没有清空只是这些标记告诉我们可以从哪里开始往Buffer里写数据。如果Buffer中有一些未读的数据调用clear()方法数据将丢弃那就没有标记说明哪些数据读过哪些还没有。如果还需要Buffer中未读的数据但是还想要先写些数据那么使用compact()方法。compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样设置成capacity。现在Buffer准备好写数据了但是不会覆盖未读的数据。 public static void main(String[] args) {/*** 通过allocate()获取缓冲区缓冲区主要有2个核心方法put()将输入存入缓冲区,get()获取缓冲区数据*/// 获取缓冲区ByteBuffer buffer ByteBuffer.allocate(1024);System.out.println(capacity: buffer.capacity()\t position:buffer.position()\t limit:buffer.limit());// 将数据存入缓冲区String str hello;buffer.put(str.getBytes());System.out.println(capacity: buffer.capacity()\t position:buffer.position()\t limit:buffer.limit());// 获取缓冲区数据,要获取缓存区的数据需要flip()切换缓冲区的模式buffer.flip();System.out.println(capacity: buffer.capacity()\t position:buffer.position()\t limit:buffer.limit());// 创建字节数据接收数据byte[] b new byte[buffer.limit()];buffer.get(b);System.out.println(new String(b,0,buffer.limit()));System.out.println(capacity: buffer.capacity()\t position:buffer.position()\t limit:buffer.limit());// rewind()可重复读buffer.rewind();System.out.println(capacity: buffer.capacity()\t position:buffer.position()\t limit:buffer.limit());} ----------------------------------- 输出结果 capacity:1024 position:0 limit:1024 capacity:1024 position:5 limit:1024 capacity:1024 position:0 limit:5 hello capacity:1024 position:5 limit:5 capacity:1024 position:0 limit:5 3.3 直接缓冲区和非直接缓冲区 缓冲区分为直接缓冲区和非直接缓存区①非直接缓冲区硬盘--系统的缓冲区--copy--JVM缓冲区--程序②直接缓冲区需要copyJVM和缓冲区实现映射。 直接字节缓冲区 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说在每次调用基础操作系统的一个本机 I/O 操作之前或之后虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中或从中间缓冲区中复制内容。  直接字节缓冲区可以通过调用ByteBuffer的 allocateDirect() 工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外因此它们对应用程序的内存需求量造成的影响可能并不明显。所以建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。一般情况下最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。  直接字节缓冲区还可以通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。该方法返回ByteBuffer的子类MappedByteBuffer 。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域则试图访问该区域不会更改该缓冲区的内容并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。  非直接缓冲区如上假设应用程序想要在磁盘中读取一些数据的话。应用程序首先发起一个请求要去读取当前物理磁盘里面的数据这个时候需要将物理磁盘的数据首先读到内核空间中然后拷贝一份到用户空间然后才能通过read的方法将数据读到应用程序中。同样的应用程序中有数据的话需要先写入到用户地址空间然后复制到内核地址空间再由内核空间写入到物理磁盘。在这个过程中这两个复制的操作比较多余所以他的效率比较低一些也就是将我们的缓冲区建立在jvm的内存中相对效率更低。 直接字节缓冲区如上图直接缓冲区不需要拷贝是将我们的数据直接在物理内存中建立一个映射文件将数据写到这个文件里面去这个时候我们应用程序要写一些数据的话直接写到这个映射文件。操作系统就会将这个写到物理磁盘中。读磁盘数据同理。这个过程就没有中间的copy就会比较高。 字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect() 方法来确定。非直接缓冲区通过allocate方法分区缓存区将缓存区建立在JVM的内存中 直接缓存区通过allocateDircet方法分区缓冲区将缓冲区建立在物理内存中效率更高。 到allocateDirect和allocate创建的源码中发现allocate创建的是一个HeapByteBufferHeap堆其实就是表示用户空间在jvm内存中创建的一个缓冲区。 public static ByteBuffer allocate(int capacity) {if (capacity 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity);}public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);} 3.4 缓冲区使用 Buffer使用一般遵循以下几个原则 ①分配空间如ByteBuffer buffer ByteBuffer.allocate(1024);或者使用allocateDirector ②将数据写入到Buffer中 int readBuffer inChannel.read(buffer); ③调用flip()方法将limit设置为当前positionposition设置为0mark设置为-1 ④从Buffer中读取数据 readBuffer inChannel.read(buffer); ⑤调用clear()将limit设置为capacityposition设置为0mark设置为-1数据没有清空方法或者compact()方法 4 通道Channel Channel表示IO源于目标节点打开的连接类似于传统的流但是Channel不直接存储数据Channel只能与Buffer进行交互。 以非直接缓冲区为例应用程序获取数据需要经过用户空间然后内核空间再读取数据所有的读取操作在NIO是直接由CPU负责的。这个流程会存在一些问题当我们有大量的文件读取操作的时候cpu他的利用就很低因为IO操作直接抢占CPU的资源就不能够去做其他的事情所以他的效率就会变低。 计算机CPU和内存的交互是最频繁的内存是我们的高速缓存区CPU运转速度越来越快磁盘远远跟不上CPU的读写速度才设计了内存。这里把CPU的连接干掉了变成了DMA(Direct Memory Access直接内存存取器)就是直接内存存储。如果要读取数据所以的操作是直接在当前DMA这里直接完成不再有CPU去进行负责。但是得到DMA还是需要由当前的CPU进行相关的调度。在这里交给了DMA之后CPU就能做其他的事但是如果依然有大量的IO操作的时候又会造成DMA总线的拥堵因为最终没有直接和CPU撇开关系。导致在大量的文件读取请求的时候依然利用率比较低这个时候就出现了新的数据读写流程这个时候就出现了channel通道。 把DMA换成了通道channel。通道channel可以认为他是一个完全独立的处理器他就是用来专门负责文件的IO操作的也就是说以后所有的数据直接交给channel去进行负责读取。这个时候CPU才算是真正的解放了。 java为Channel接口提供的最主要的实现类如下①FileChannel用于读取写入、映射和操作文件的通道②SocketChannel通过TCP读取网络中的数据③ServerSocketChannel可以监听新进来的TCP连接对每个新进来的连接都会创建一个SocketChannel④DatagramChannel通过UDP读写网络中的数据通道 获取通道的三种方式①对支持通道的对象调用getChannel()支持通道的类有FileInputStreamFileOutputStreamRandomAccessFileSocketServerSocketDatagramSocket②通过XxxChannel的静态方法open()打开并返回指定的XxxChannel③使用Files工具类的静态方法newByteChannel()获取字节通道。 FileChannel常用方法 方法描述int read(ByteBuffer dst)从Channel中读取数据到ByteBufferlong read(ByteBuffer[] dsts)将Channel中的数据“分散”到ByteBuffer[]int write(ByteBuffer src)将ByteBuffer的数据写入到Channellong write(ByteBuffer[] srcs)将ByteBuffer[]的数据聚集到ChannelMappedByteBuffer map(MapMode mode,long position,long size)将Channel对应的部分数据或者全部数据映射到ByteBufferlong position()返回次通道的文件位置FileChannel position(long p)设置此通道的文件位置long size()返回此通道的文件大小FileChannel truncate(long s)将此通道的文件截取为给定大小void force(boolean metadata)强制将所有对此通道的文件更新写入到存储设备中 分散(Scatter)读取和聚集(Gather)写入①分散读取Scattering Reads是指从Channel中读取的数据“分散”到多个Buffer中注意按照缓冲区的顺序从 Channel 中读取的数据依次将Buffer填满。②聚集写入Gathering Writes是指将多个Buffer中的数据“聚集”到Channel注意按照缓冲区的顺序写入position和limit之间的数据到Channel。 NIO的强大功能部分来自于Channel的非阻塞特性套接字的某些操作可能会无限期地阻塞。如对accept()方法的调用可能会因为等待一个客户端连接而阻塞对read()方法的调用可能会因为没有数据可读而阻塞直到连接的另一端传来新的数据。总的来说创建/接收连接或读写数据等I/O调用都可能无限期地阻塞等待直到底层的网络实现发生了什么。慢速的有损耗的网络或仅仅是简单的网络故障都可能导致任意时间的延迟。然而不幸的是在调用一个方法之前无法知道其是否阻塞。NIO的channel抽象的一个重要特征就是可以通过配置它的阻塞行为以实现非阻塞式的信道。 channel.configureBlocking(false) 在非阻塞式信道上调用一个方法总是会立即返回。这种调用的返回值指示了所请求的操作完成的程度。例如在一个非阻塞式ServerSocketChannel上调用accept()方法如果有连接请求来了则返回客户端SocketChannel否则返回null。 对比传统IO和NIO的代码 /*** 传统IO*/public static void IO_FileInputStream(){BufferedInputStream bis null;BufferedOutputStream bos null;try {bis new BufferedInputStream(new FileInputStream(new File(a.txt)));bos new BufferedOutputStream(new FileOutputStream(new File(b.txt)));byte[] buffer new byte[1024];int len;while ((lenbis.read(buffer))!-1){bos.write(buffer,0,len);bos.flush();}} catch (IOException e) {e.printStackTrace();}finally {try {if(bis ! null){bis.close();}if(bos ! null){bos.close();}} catch (IOException e) {e.printStackTrace();}}}/*** NIO*/public static void NIO_FileInputStream(){FileInputStream fis null;FileOutputStream fos null;try {fis new FileInputStream(new File(a.txt));fos new FileOutputStream(new File(b.txt));FileChannel inChannel fis.getChannel();FileChannel outChannel fos.getChannel();ByteBuffer buffer ByteBuffer.allocate(1024);int readBuffer inChannel.read(buffer);while (readBuffer!-1){buffer.flip();while(buffer.hasRemaining()){outChannel.write(buffer);}buffer.compact();readBuffer inChannel.read(buffer);}} catch (IOException e) {e.printStackTrace();}finally{try{if(fis ! null){fis.close();}if(fos ! null){fos.close();}}catch (IOException e){e.printStackTrace();}}} 对比直接缓冲区与内存映射文件操作 public class NioTest {public static void main(String[] args) {nioBuffer();nioDirectBuffer();}private static void nioBuffer() {long start System.currentTimeMillis();FileChannel inChannel null;FileChannel outChannel null;try {// 获取通道inChannel FileChannel.open(Paths.get(D:\\test\\doneFile0comlog_20201117_01.log.gz), StandardOpenOption.READ);outChannel FileChannel.open(Paths.get(D:\\test\\doneFile0comlog_20201117_01.log.gz.bak),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);// 创建缓冲区ByteBuffer buffer ByteBuffer.allocate(1024);// 将输入通道的数据写入缓冲区while (inChannel.read(buffer)!-1){buffer.flip();// 将缓冲区数据写入输出通道outChannel.write(buffer);// 清空缓冲区buffer.clear();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {if (outChannel!null){try {outChannel.close();} catch (IOException e) {e.printStackTrace();}}if (inChannel!null){try {inChannel.close();} catch (IOException e) {e.printStackTrace();}}}long end System.currentTimeMillis();System.out.println(nioBuffer:(end-start));}private static void nioDirectBuffer() {long start System.currentTimeMillis();FileChannel inChannel null;FileChannel outChannel null;try {// 获取通道inChannel FileChannel.open(Paths.get(D:\\test\\doneFile0comlog_20201117_01.log.gz), StandardOpenOption.READ);outChannel FileChannel.open(Paths.get(D:\\test\\doneFile0comlog_20201117_01.log.gz.bak),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);// 进行内存映射文件MappedByteBuffer inMapBuffer inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMapBuffer outChannel.map(FileChannel.MapMode.READ_WRITE, 0, outChannel.size());// 对缓冲区进行读写操作byte[] b new byte[inMapBuffer.limit()];inMapBuffer.get(b);outMapBuffer.put(b);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (outChannel ! null) {try {outChannel.close();} catch (IOException e) {e.printStackTrace();}}if (inChannel ! null) {try {inChannel.close();} catch (IOException e) {e.printStackTrace();}}}long end System.currentTimeMillis();System.out.println(nioDirectBuffer: (end - start));}}---------------------------------- 结果为 nioBuffer:94 nioDirectBuffer:7 5 选择器Selector 5.1 相关概念 NIO和BIO有一个非常大的区别是BIO是阻塞的NIO是非阻塞的。阻塞与非阻塞是相对于网络通信而言的。网络通信就会有客户端的概念。客户端要向服务端发送数据的话必须建立连接在这个过程中会做一些相关的事情如accpet等待连接然后客户端write数据服务端read数据。这些操作在传统的套接字socket里面都是阻塞式的。服务端一次只能接待一个客户端不能一下多个的客户端。也就是客户端请求服务器做些事情的时候这个客户端没有处理完其他客户端的请求是进不来的。这种就是阻塞式的所以服务端如果是这种模型的话他的效率是非常低的。 要解决这种阻塞就要通过多线程的方式解决但是线程资源是有限的那就极大的限制了服务端他的处理效率。这就是经典的C10K问题假如有C10K就需要创建1W个进程。在NIO中非阻塞的网络通信模型Selector就能解决这个问题。 系统线程的切换是消耗系统资源的如果我们每一个连接都用一个线程来管理资源的开销会非常大这个时候就可以用Selector。通过Selector可以实现一个线程管理多个Channel如果你的应用打开了多个通道但每个连接的流量都很低使用Selector就会很方便。例如在一个聊天服务器中。要使用Selector, 得向Selector注册Channel然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回线程就可以处理这些事件如新的连接进来、数据接收等。Selector 的意义在于只通过一个线程就可以管理成千上万个 I/O 请求 相比使用多个线程避免了线程上下文切换带来的开销。 Selector是怎么工作的呢有了Selector之后Selector会把每一个客户端和服务端传输数据的通道都到Selector上去注册一下。也就是以后你想向服务端发送数据通道先到Selector选择器上注册一下那么Selector就会监控当前channel的IO状况读写连接接受处理等情况只有当某个channel上的数据完全准备就绪Selector才会把这样一个channel里面的任务分配到服务端来进行运行。当我们客户端要给服务端发送数据的时候channel需要在Selector上进行注册当channel的数据完全准备就绪的时候Selector才会将任务分配给服务端的一个线程进行处理。这种非阻塞式的相较于阻塞式的就能非常好的利用cpu的资源提高cpu的工作效率。 一个Selector实例可以同时检查一组信道的I/O状态。用专业术语来说选择器就是一个多路开关选择器因为一个选择器能够管理多个信道上的I/O操作。然而如果用传统的方式来处理这么多客户端使用的方法是循环地一个一个地去检查所有的客户端是否有I/O操作如果当前客户端有I/O操作则可能把当前客户端扔给一个线程池去处理如果没有I/O操作则进行下一个轮询当所有的客户端都轮询过了又接着从头开始轮询这种方法是非常笨而且也非常浪费资源因为大部分客户端是没有I/O操作我们也要去检查而Selector就不一样了它在内部可以同时管理多个I/O当一个信道有I/O操作的时候他会通知SelectorSelector就是记住这个信道有I/O操作并且知道是何种I/O操作是读呢是写呢还是接受新的连接所以如果使用Selector它返回的结果只有两种结果一种是0即在你调用的时刻没有任何客户端需要I/O操作另一种结果是一组需要I/O操作的客户端这时你就根本不需要再检查了因为它返回给你的肯定是你想要的。这样一种通知的方式比那种主动轮询的方式要高效得多。 使用选择器首先创建一个Selector实例使用静态工厂方法open()并将其注册register到想要监控的信道上通过channel的方法实现而不是使用selector的方法。最后调用选择器的select()方法。该方法会阻塞等待直到有一个或更多的信道准备好了I/O操作或等待超时。select()方法将返回可进行I/O操作的信道数量。现在在一个单独的线程中通过调用select()方法就能检查多个信道是否准备好进行I/O操作。如果经过一段时间后仍然没有信道准备好select()方法就会返回0并允许程序继续执行其他任务。 Selector 只能与非阻塞模式下的通道一起使用即需要实现 SelectableChannel 接口否则会抛出 IllegalBlockingModeException 异常 5.2 Selector使用 1使用步骤 ①创建Selector ②向Selector注册通道一个Selector可以注册多个通道 ③通过Selector选择就绪的通道 // 通过open()方法创建 SelectorSelector selector Selector.open();// 创建一个通道以ServerSockeetChannel为例,并且将通道设置为非阻塞模式ServerSocketChannel channel ServerSocketChannel.open();channel.configureBlocking(false);// 通过register()方法注册通道channel.register(selector, SelectionKey.OP_ACCEPT);// 通过select()方法从多个通道中以轮询的方式选择已经准备就绪的通道。根据之前register()方法中设置的兴趣将可以进行对应操作的通道选择出来selector.select();// 通过Selector的selectedKeys()方法获得已选择键集(selected-key set)Set key selector.selectedKeys();//通过Iterator迭代器依次获取key中的SelectionKey对象并通过SelectionKey中的判断方法执行对应的操作IteratorSelectionKey iterator key.iterator();while (iterator.hasNext()) {SelectionKey selectionKey iterator.next();if (selectionKey.isAcceptable()) {//TODO}if (selectionKey.isReadable()){//TODO}if(selectionKey.isWritable()key.isValid()){//TODO}if (selectionKey.isConnectable()){//TODO}iterator.remove();} 2register()方法 public abstract SelectionKey register(Selector sel, int ops, Object att)throws ClosedChannelException; register() 方法返回SelectionKey对象在SelectableChannel抽象类中定义如上。参数说明如下 Selector sel 通道注册的选择器int opsinterest集合表示通过Selector监听Channel时对什么事件感兴趣Object att这是一个可选参数在注册通道时可以附加一个对象用于之后便于识别某个通道 interest集合有下面4种操作 操作类型值描述SelectionKey.OP_ACCEPT14接收Socket操作SelectionKey.OP_READ10读操作SelectionKey.OP_WRITE12写操作SelectionKey.OP_CONNECT13接收Socket操作 注意通道一般并不会同时支持这四种操作类型我们可以通过 validOps() 方法获取通道支持的类型。 3select()方法 select有2个重载方法 ①int select()选择已准备就绪的通道返回值表示自上一次选择后有多少新增通道准备就绪当没有通道准备就绪时会一直阻塞下去直到至少一个通道被选择、该选择器的 wakeup() 方法被调用或当前线程被中断时。select() 方法实际上调用了 select(0L) 方法返回 ②int select(long timeout)选择已准备就绪的通道当没有通道准备就绪时会一直阻塞下去直到至少一个通道被选择、该选择器的 wakeup() 方法被调用、当前线程被中断或给定时间到期时返回。 除此紫外还可以选择 selectNow() 方法该方法为非阻塞方法无论有无通道就绪都会立即返回。如果自前一次 select 操作后没有新的通道准备就绪则会立即返回 0。 4SelectionKey SelectionKey中有下面几种判断方法与操作类型相对应 boolean isReadable()是否可读是返回 trueboolean isWritable()是否可写是返回 trueboolean isConnectable()是否可连接是返回 trueboolean isAcceptable() 是否可接收是返回 true selectedKeys() 获得的是已就绪的通道对应的 SelectionKey。如果想获得该选择器上所有通道对应的 SelectionKey可以通过 keys() 方法获取。 5使用例子 public class NIOServer {public static void main(String[] args) throws IOException {// 获取通道并设置为非阻塞ServerSocketChannel ssChannel ServerSocketChannel.open();ssChannel.configureBlocking(false);// 绑定端口号ssChannel.bind(new InetSocketAddress(9999));// 创建选择器对象Selector selector Selector.open();// 将通道注册到选择器上那么选择器就会监听通道的接收时间如果有接收并且接收准备就绪才开始进行下一步操作ssChannel.register(selector, SelectionKey.OP_ACCEPT);// 通过轮训的方式获取选择器上准备就绪的事件// selector.select()0表示至少有个selectionKey准备就绪while (selector.select()0){// 获取当前选择器中所有注册的选择键IteratorSelectionKey iterator selector.selectedKeys().iterator();// 迭代获取已经准备好的选择键while (iterator.hasNext()){// 获取已经准备就是的事件SelectionKey sk iterator.next();if(sk.isAcceptable()){// 调用accpetSocketChannel sChannel ssChannel.accept();// 将sChannel设置为非阻塞的sChannel.configureBlocking(false);// 将该通道注册到选择器上sChannel.register(selector,SelectionKey.OP_READ);}else if(sk.isReadable()){// 如果读状态已经准备就是那么开始读取数据// 获取当前选择器上读状态准备就绪的通道SocketChannel sChannel (SocketChannel)sk.channel();// 创建缓冲区接收客户端发送过来的数据ByteBuffer buffer ByteBuffer.allocate(1024);// 读取缓冲区的数据int len 0;while ((lensChannel.read(buffer))0){buffer.flip();System.out.println(new String(buffer.array(),0,len));buffer.clear();}}// 当selectKey使用完之后要溢出否则会一直优先iterator.remove();}}} }public class NIOClient {public static void main(String[] args) throws IOException {// 获取通道默认是阻塞的SocketChannel sChannel SocketChannel.open(new InetSocketAddress(127.0.0.1, 9999));// 设置通道为非阻塞的sChannel.configureBlocking(false);// 创建缓冲区ByteBuffer buffer ByteBuffer.allocate(1024);buffer.put(hello.getBytes());// 将缓冲区数据写入到sChannel中buffer.flip();sChannel.write(buffer);buffer.clear();sChannel.close();} }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/925388.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频多的网站建设如何建设一个静态网站

什么是柔性数组 什么是柔性数组?柔性数组其实也是动态内存管理部分的内容。这节主要来复习柔性数组的知识点。 当结构体的最后一个成员为数组, 且这个数组的大小未确定时, 我们就称它是柔性数组。 如: struct stu{char name[…

泰康人寿保险官方网站专题学习网站模板

boot读取配置文件1. yml配置文件2. 绑定方式13. 绑定方式23.1 依赖.3.2 boot 中提供的属性装配功能。1. 在某些业务中,需要将类的配置写到配置文件中, 不在代码中写死配置。 2. 所以需要读取配置文件(yaml, properties) 读取到Java Bean中。 3. 本文以oss对象存储配…

别样的国庆作业大战

一天,文科给我(理科)打来电话。他说:“你敢不敢和我举行假期作业量大战?”我豪爽的答应了:“我当然敢!我有 \(10^{12180211} \operatorname{mol}\) 试卷!2025.10.1 在 XXX 中高一年级举行,谁不来谁就是怂货。…

淘宝网站的建设目的是什么意思长沙网站托管seo优化公司

近期在复习Postman的基础知识,在小破站上跟着百里老师系统复习了一遍,也做了一些笔记,希望可以给大家一点点启发。 1、目前市面上的加密的方式 对称式加密:DES,AES,Base64加密算法 非对称加密&#xff1a…

摄影网站设计思想宁波优质网站制作哪家好

linux 常用的帮助命令 1.help -相当于man的简化版 例如:vim –help 2.man: 详细介绍vim的用法 例如:man vim 3.info:偏重介绍文档 例如: info vim

ROS2之服务

ROS2 的服务(Service)概念:服务是一种 请求-响应(request-response)通信机制,由 客户端(Client) 和 服务端(Server) 两部分组成。通信模式:客户端 发送一个请求(Request)。服务端 处理请求,并返回响应(…

北京公司建网站要多少费用江门站官网

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k &#xff0c;请你返回满足 0 < i < j < n &#xff0c;nums[i] nums[j] 且 (i * j) 能被 k 整除的数对 (i, j) 的 数目 。 示例 1&#xff1a; 输入&#xff1a;nums …

macOS上优雅运行Docker容器

colima 什么是colima colima主页 Colima(Container on Lima)是一个轻量级的容器运行时管理工具,专为macOS(同时也支持Linux)设计,提供了一种简单优雅的方式来运行容器。它基于Lima项目,后者为 macOS提供了类似W…

怎样模仿别人的网站wordpress免插件代码高亮

TCP/IP协议—TCP TCP协议TCP通信特点TCP技术概念TCP定时器 TCP头部报文TCP连接三次握手&#xff08;建立连接&#xff09;四次挥手&#xff08;释放连接&#xff09;连接状态 TCP协议 传输控制协议&#xff08;TCP&#xff0c;Transmission Control Protocol&#xff09;是一种…

使用IOT-Tree Server依据MC协议连接三菱Q系列PLC

使用IOT-Tree Server依据MC协议连接三菱Q系列PLC2025-10-02 22:40 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display…

长春网站设计团队html自我介绍网页模板

1. 基本架构 ovs-vsctl: 管理ovsdb-server的配置&#xff0c;提供OVSDB的配置方法&#xff0c;包括创建和删除网桥、端口等&#xff1b; ovs-ofctl: 提供ovs-vswitchd的流表配置方法&#xff1b; ovs-dpctl: 配置OVS内核模块&#xff0c;提供缓存流表的操作方法&#xff1b…

【Linux】【硬件向】从“找文件”到“懂磁盘”:一文搞懂文件、磁盘与文件系统 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

题解:CF1770H Koxia, Mahiru and Winter Festival

牛牛题。 题意:给出两个排列 \(p,q\),要求构造一种路径方案,\((1,i)\rightarrow(n,p_i)\),\((i,1) \rightarrow(q_i, n)\),要求经过次数最大的边经过次数最少。 做法: 首先 \(p_i=i,q_i=i\) 直接就是 \(1\),轻松…

HarmonyOS之LocalStorage - 详解

HarmonyOS之LocalStorage - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&qu…

南华 NHXJ-02 汽车悬架检验台:实用的技术特性与实操应用指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

建设银行网站理财产品安徽房地产网站建设

pack://application:, pack://application:, 是一个在 WPF (Windows Presentation Foundation) 应用程序中用于指定资源位置的 URI (统一资源标识符) 方案的特定格式。这个格式用于访问嵌入在应用程序程序集&#xff08;assemblies&#xff09;中的资源&#xff0c;如图像、XA…

网站建设后如何修改wordpress 投稿 标签

28-实现 strStr()&#xff08;easy&#xff09; 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 思路…

网站重定向淮北公司做网站

一、对插槽的理解 1.定义及作用&#xff1a; 插槽是一种用于在组件中插入内容的特殊语法。它的作用是让父组件可以向子组件传递内容&#xff0c;从而实现组件的灵活性和复用性。 2.分类&#xff1a; 插槽可以分为具名插槽和作用域插槽。 2.1具名插槽&#xff1a; 具名插槽允许父…

怎么做网站注册登入页面成都网站建设公

统一配置管理中心:TranslationChain 架构的简洁配置管理方案 1. 集中式配置文件设计 config/settings.yaml: # 多环境配置开关 env: production # development|test|production# 模型管理中心 models:openai:class: langchain_openai.ChatOpenAIparams

Spring Boot Logback:实现定时任务日志与业务日志隔离 - Higurashi

1. 问题背景 在 Spring Boot 应用中,我们通常使用@EnableScheduling启用定时任务。这些定时任务在执行过程中,可能会调用 Mapper 方法与数据库交互,产生大量的 SQL 日志。默认情况下,这些日志会与普通业务请求的日…