当前位置: 首页 > news >正文

深圳网站设计是什么网站建设背景图

深圳网站设计是什么,网站建设背景图,做民宿上几家网站好,wordpress mysql5.7文章目录 1. 问题引入2. NIO的工作方式3. Buffer的工作方式4. NIO数据访问方式 1. 问题引入 在网络通信中#xff0c;当连接已经建立成功#xff0c;服务端和客户端都会拥有一个Socket实例#xff0c;每个Socket实例都有一个InputStream和OutputStream#xff0c;并通过这… 文章目录 1. 问题引入2. NIO的工作方式3. Buffer的工作方式4. NIO数据访问方式 1. 问题引入 在网络通信中当连接已经建立成功服务端和客户端都会拥有一个Socket实例每个Socket实例都有一个InputStream和OutputStream并通过这两个对象来交换数据。同时我们也知道网络I/O都是以字节流传输的当创建Socket对象时操作系统会为InputStream和OutputStream分别分配一定大小的缓存区数据的写入都是通过这个缓存区完成的。写入端将数据写到SendQ队列中当队列填满时数据将被转移到另一端的InputStream的RecvQ队列中如果这时RecvQ已经满了那么OutputStream的write方法将会阻塞直到RecvQ队列有足够的空间容纳SendQ发送的数据。值的注意的是这个缓存区的大小及写入端的速度和读取端的速度非常影响这个连接的数据传输效率由于可能发生阻塞所以网络I/O与磁盘I/O不同的是数据的写入和读取还要有一个协调的过程如果两边同时传送数据可能会产生死锁。使用NIO可以解决该问题。 死锁产生的根本原因是 客户端和服务端都需要对方释放资源例如在缓存队列中写入数据。当缓存队列满时写操作会被阻塞但同时双方都可能需要对方的缓存队列空间这样就形成了资源争用的情况 2. NIO的工作方式 BIO带来的挑战 BIO即阻塞I/O不管是磁盘I/O还是网络I/O数据载写入OutputStream或者从InputStream读取时都可能被阻塞一旦被阻塞线程将会失去CPU的使用权这在当前大规模访问量和有性能要求的情况下时不能被接受的。虽然当前的网络I/O有一些解决方案如一个客户端一个处理线程出现阻塞时只能是一个线程阻塞而不会影响其他线程工作还有为了减少系统线程的开销采用线程池的办法来减少线程创建和回收的成本。单如果当前需要大量的HTTP长连接的情况线程池可能无法创建那么多线程来保持连接。所以此时我们需要一种新的I/O操作方式。 磁盘IO场景 在磁盘 I/O 中阻塞通常发生在数据的读取和写入过程中。假设一个应用程序需要从磁盘读取大量数据 读取阻塞 当应用程序发起读取请求时系统会将请求发送给磁盘驱动器然后等待磁盘驱动器将数据加载到内存中。在这个过程中应用程序的线程会被阻塞直到读取操作完成。如果读取的数据量较大阻塞时间可能会显著增加。写入阻塞 类似地当应用程序发起写入请求时系统会将数据传输到磁盘然后等待写入操作完成。写入操作的阻塞时间取决于数据的大小和磁盘的性能。如果写入的数据量很大应用程序可能会长时间地被阻塞。 网络IO场景 在网络 I/O 中BIO 模型同样存在阻塞问题。考虑一个基于阻塞 I/O 的服务器应用程序它接受客户端连接并处理数据 接受连接阻塞 当服务器调用accept函数等待客户端连接时如果没有客户端连接进来该调用会一直阻塞。在这段时间内服务器的线程无法执行其他任务造成资源浪费。读取数据阻塞 在已经建立连接的情况下当服务器调用read函数等待接收客户端发送的数据时如果没有数据到达该调用会一直阻塞。服务器线程被迫等待可能会导致性能下降。写入数据阻塞 类似地当服务器调用write函数将数据发送到客户端时如果客户端接收缓冲区已满写入操作也会阻塞。这可能使得服务器线程长时间处于等待状态。 NIO工作机制 Java IO和NIO的主要区别在于两者的处理方式不同。Java IO是面向流Stream的它将输入输出数据直接传输到目标设备或文件中以流的形式进行读写而NIO则是面向缓冲区Buffer的它将会使用缓存去管理数据使得读写操作更加快速和灵活。 Buffer类 在 Java NIO 中Buffer 类是一个抽象类表示一个数据缓冲区用于在通道Channel和原始数据之间进行数据传输。主要的 Buffer 子类包括 ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer 和 DoubleBuffer它们分别对应不同的基本数据类型。 public abstract class Buffer { //mark 属性和 reset() 方法一起使用用于在缓冲区中设置一个标记mark位置并在之后通过 reset() 方法将当前位置重置为这个标记位置。mark 属性的作用是用于记录一个位置以便后续能够回到该位置方便重新处理或采取其他措施。private int mark -1;//当前位置的索引private int position 0;//限制位置的索引即缓冲区中数据的有效长度private int limit;//缓存区的容量private int capacity;//内存段的代理对象final MemorySegmentProxy segment;//该方法用于设置新的索引位置public Buffer position(int newPosition) {//如果新的索引位置大于数据的有效长度或小于0抛出异常if (newPosition limit | newPosition 0)throw createPositionException(newPosition);//如果新的索引位置在mark标记之后那么标记置为-1if (mark newPosition) mark -1;//然后将当前索引位置设置为新的索引位置position newPosition;return this;}//设置新的有效数据长度public Buffer limit(int newLimit) {//如果新的有效数据长度大于缓冲区最大容量或小于0抛出异常if (newLimit capacity | newLimit 0)throw createLimitException(newLimit);//将有效数据长度设置为新的有效数据长度limit newLimit;//如果当前位置所以大于新的有效数据长度那么当前位置索引同样设置为有效数据长度if (position newLimit) position newLimit;//mark如果大于新的有效数据长度则直接设置为无效if (mark newLimit) mark -1;return this;}//用于标记当前索引位置public Buffer mark() {mark position;return this;}//将当前索引位置重置为mark标记所在的位置public Buffer reset() {int m mark;if (m 0)throw new InvalidMarkException();position m;return this;}//清空缓存区数据实际上就是重置了mark、position和limitpublic Buffer clear() {position 0;limit capacity;mark -1;return this;}//flip() 方法可以将缓冲区从写模式切换到读模式。调用 flip() 后位置被设置为0限制被设置为之前的位置用于准备读取缓冲区中的数据。public Buffer flip() {//limit有效数据长度为当前索引所在位置limit position;//postion设置为0表示从0开始读取position 0;mark -1;return this;}//将缓冲区的位置设置为 0限制保持不变用于重新读取缓冲区中的数据类似于 flip() 但不改变限制。public Buffer rewind() {position 0;mark -1;return this;}//获取buffer中剩余的有效容量public final int remaining() {int rem limit - position;return rem 0 ? rem : 0;}}Channel类 Java NIO 中Channel 是一个接口它提供了用于读取和写入数据的统一的 API。Channel 接口是 NIO 用于与 I/O 设备如文件、套接字、选择器等交互的核心部分。Channel 接口的实现类可以包括文件通道、套接字通道等。 public interface Channel extends Closeable { //判断通道是否打开public boolean isOpen();//关闭通道public void close() throws IOException; }Seletionkey类 SelectionKey 类是 Java NIO 中的关键类它用于表示注册到 Selector 上的通道和对应的事件。SelectionKey 对象是 Selector 与通道之间的桥梁它包含了与通道相关的一些信息以及通道所感兴趣的事件以便在选择器上进行有效的事件选择。 选择键则是一种将通道和选择器进行关联的机制。 public abstract class SelectionKey { //SelectableChannel 是 Java NIO 中表示支持非阻塞模式的通道的抽象类该返回返回一个通道public abstract SelectableChannel channel();//该方法返回一个选择器public abstract Selector selector();//获取通道所感兴趣的操作集合事件集合返回一个位掩码用于表示关注的事件public abstract int interestOps();//读操作的位掩码用于表示通道已经准备好进行读操作。public static final int OP_READ 1 0;//写操作的位掩码用于表示通道已经准备好进行写操作。public static final int OP_WRITE 1 2;//连接操作的位掩码 用于表示连接已经建立public static final int OP_CONNECT 1 3;//accept操作的位掩码 用于表示通道已经准备好接受新的连接。public static final int OP_ACCEPT 1 4;//返回通道现在是否可读public final boolean isReadable() {return (readyOps() OP_READ) ! 0;}//通道现在是否可写public final boolean isWritable() {return (readyOps() OP_WRITE) ! 0;}//通道是否已经连接就绪public final boolean isConnectable() {return (readyOps() OP_CONNECT) ! 0;}//通道是否可以接受新的连接public final boolean isAcceptable() {return (readyOps() OP_ACCEPT) ! 0;}//将指定的对象附加到此键public final Object attach(Object ob) {return ATTACHMENT.getAndSet(this, ob);}}Selector类 Selector 是 Java NIO 中的一个关键类用于实现非阻塞 I/O 操作的多路复用。通过 Selector可以在单个线程上同时监控多个通道的事件从而实现高效的事件驱动编程模型。 public abstract class Selector implements Closeable {//返回新创建的选择器实例public static Selector open() throws IOException {return SelectorProvider.provider().openSelector();}//判断selector是否已经打开public abstract boolean isOpen();//SelectorProvider 是一个抽象类用于提供 Selector 和 Channel 的创建。每个 Selector 都与一个 SelectorProvider 实例关联而SelectorProvider 实例的具体实现是由具体的操作系统提供的。public abstract SelectorProvider provider();//获取选择器上所有的键集public abstract SetSelectionKey keys();//关闭选择器public abstract void close() throws IOException;// 阻塞直到至少有一个通道在选择器上准备好进行 I/O 操作或者调用线程被中断。返回已经准备就绪的通道的数量。public abstract int select() throws IOException;//非阻塞地检查是否有通道准备好进行 I/O 操作。返回已经准备就绪的通道的数量。public abstract int selectNow() throws IOException;//唤醒因为调用 select 或 selectNow 方法而处于阻塞状态的线程。返回调用 wakeup 方法的选择器。public abstract Selector wakeup(); }关键类就是Channel和Selector它们是NIO中的两个核心概念。Channel 通过 register 方法可以注册到 Selector 中以实现非阻塞 I/O 操作。Channel 和 Buffer 之间通过 read 和 write 方法进行数据的传输。数据首先被写入到缓冲区然后从缓冲区读取到通道或从通道读取到缓冲区。Channel 可以通过 register 方法注册到 Selector 上注册时需要指定感兴趣的事件例如读、写等。SelectionKey 对象表示了一个通道在一个选择器上的注册信息它与通道和选择器之间建立了关联。SelectionKey 包含了通道、选择器、感兴趣的操作集合事件集合、附件等信息。 下面看看NIO是如何工作的 import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set;public class NioServer {public static void main(String[] args) {try (ServerSocketChannel serverSocketChannel ServerSocketChannel.open() //创建一个通道) {//将通道绑定到一个指定的端口ServerSocketChannel使用选择器Selector来管理多个通道可以在一个线程中处理多个通道的连接请求。serverSocketChannel.bind(new InetSocketAddress(8080)); //将通道设置为非阻塞模式serverSocketChannel.configureBlocking(false);//创建一个选择器Selector selector Selector.open();//将通道注册到选择器中状态是等待接受客户端连接serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println(NIO Server started on port 8080);while (true) {//自旋获取准备好的通道int readyChannels selector.select();if (readyChannels 0) {continue;}//获取所有的键集SetSelectionKey selectedKeys selector.selectedKeys();//迭代键集IteratorSelectionKey keyIterator selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key keyIterator.next();//处理相应事件if (key.isAcceptable()) {handleAcceptEvent(key, selector);} else if (key.isReadable()) {handleReadEvent(key);}keyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}}private static void handleAcceptEvent(SelectionKey key, Selector selector) throws IOException {//获取键所关联的通道ServerSocketChannel serverSocketChannel (ServerSocketChannel) key.channel();//准备接受连接SocketChannel clientChannel serverSocketChannel.accept();//设置通道模式为非阻塞clientChannel.configureBlocking(false);//通道注册到选择器上状态为可以读取数据clientChannel.register(selector, SelectionKey.OP_READ);System.out.println(Accepted connection from clientChannel);}private static void handleReadEvent(SelectionKey key) throws IOException {SocketChannel clientChannel (SocketChannel) key.channel();//分配缓存区ByteBuffer buffer ByteBuffer.allocate(1024);//通道读取int bytesRead clientChannel.read(buffer);if (bytesRead -1) {System.out.println(Connection closed by client: clientChannel);clientChannel.close();} else if (bytesRead 0) {buffer.flip();clientChannel.write(buffer);}} }上面代码用NIO实现了一个简单的服务器 调用Selector的静态工厂方法创建一个选择器这个静态工厂是操作系统底层实现的。然后创建一个服务端Channel绑定到一个Socket对象并把这个通道注册到选择器上把这个通道设置为非阻塞模式然后就可以调用Selector的selectedKeys方法检查已经注册在这个选择器上的所有通信信道是否有需要事件发生从而可以读取通信的数据而这里读取的数据是Buffer这个Buffer是我们可以控制的缓冲区。在上面这段程序中将server的监听连接请求和事件吹了放在一个线程中但是在事件应用中我们通常会放在两个线程中一个线程专门监听客户端的连接请求而且是以阻塞的方式执行的另一个线程专门负责处理请求这个专门处理请求的线程才会真正采用NIO的方式。 下图展示量NIO的工作方式Selector可以监听一组通信信道上的I/O状态前提是这个Selector已经注册到这些通信通道中选择器Selector可以调用select()方法检查已经注册的通信信道上I/O是否已经准备好如果没有至少一个信道I/O状态发生变化那么select方法会阻塞等待或在超时时间后返回0。如果有多个信道有数据那么将会把这些数据分配到对应的数据Buffer中。所以关键的地方是有一个线程来处理所有连接的数据交互每个链接的数据交互不是阻塞方式的所以可以同时处理大量的连接请求。 总结 NIO模式下客户端和服务端通信流程对比如下 1.服务端初始化 服务端创建一个 ServerSocketChannel并绑定到一个特定的端口。将 ServerSocketChannel 设置为非阻塞模式并注册到一个 Selector 上以监听连接事件。 2.客户端初始化 客户端创建一个 SocketChannel连接到服务端的地址。将 SocketChannel 设置为非阻塞模式并注册到一个 Selector 上以监听连接事件。 3.事件循环 服务端和客户端都进入一个事件循环不断地检查发生的事件。在服务端可能会检查 OP_ACCEPT 事件表示有新的连接请求。在客户端可能会检查 OP_CONNECT 事件表示连接已建立。这里如果一直没有客户端访问程序陷入空转并不会阻塞这样也就避免了内核切换 4.处理连接事件 在服务端当有新的连接请求到达时通过 ServerSocketChannel.accept() 接受连接并将新的 SocketChannel 注册到 Selector 上监听读事件。 5.客户端处理连接事件 在客户端当连接建立完成时通过 SocketChannel.finishConnect() 完成连接然后注册到 Selector 上监听读事件 6.处理读事件 在服务端和客户端当通道可读时从通道中读取数据 这样通过 NIO服务端和客户端可以通过非阻塞的方式处理多个连接并在一个事件循环中实现数据的读取和写入。在实际应用中可能需要结合多线程、线程池等机制以更好地处理多个连接的并发处理。 3. Buffer的工作方式 通过前面分析源码我们也大致知道了Buffer的工作方式Buffer可以简单理解为一组基本数据类型的元素列表它通过介个变量来保持这个数据当前的位置状态也就是有四个索引 我们通过ByteBuffer.allocate(11)方法创建了一个11个byte的数组缓冲区初始状态如下所示position为0capacity和limit都是数组默认长度。 当我们写入五个数据后位置如下所示。 调用flip方法数组切换状态为读状态 此时底层操作系统可以从缓存区中正确读取这5个字节数据并发送出去了在下一次写数据之前我们再调用一下clear方法缓冲区又回回到默认位置。mark标记就是记录当前position的前一个位置我们调用reset时position会恢复到mark位置。 通过Channel获取的I/O数据首先要经过操作系统的Socket缓冲区缓冲区再讲数据复制到Buffer中从操作系统缓冲区到用户缓冲区比较消耗性能Buffer提供了另外一种直接操作操作系统缓冲区的方法即ByteBuffer.allocateDirector(size);该方法返回与底层存储空间关联的缓冲区它通过Native代码操作非JVM堆的内存空间每次创建和释放的时候都会调用一次System.gc()。 4. NIO数据访问方式 我们知道当我们通过IO读取磁盘数据时需要使用操作系统的系统调用方法而这会涉及用户态到内核态的切换操作系统需要把数据读取到内核态空间然后将数据送到用户态这是十分重的一个操作NIO提供了两个优化方法: FIleChannel.transferXXX 减少数据从内核到用户空间中的复制数据直接在哪和空间中移动下图首先是传统数据访问方式然后是tansferXXX方式 FileChannel.map 它将文件按照一定大小块映射为内存区域当程序访问这个内存区域时直接操作这个文件数据这种方式就直接跳过了数据从内核空间向用户空间复制。
http://www.pierceye.com/news/318960/

相关文章:

  • 手机网站需要多少钱做淘宝网站运营工作流程
  • 惠州seo网站管理个人网站名
  • 大型网站的优化方法儿童编程哪家培训机构好
  • 怎么样能够为一个网站做推广金安合肥网站建设专业
  • 免费手机网站商城微信公众号对接网站做
  • 用vs2013做网站公司网站突然404
  • 东莞建站模板搭建广东商城网站建设
  • crm网站下载网站建设网址网站制作
  • 网站开发怎么入驻京东花店网站开发参考文献
  • 郑州专业网站推广优化公司技术支持 东莞网站建设
  • 苏州做网站的公司哪家最好网站企业
  • 厦门做网站seo网络营销就是什么
  • 哪个网站可以学做蛋糕网络软件系统
  • 网站制作的核心要点是什么找人做网站服务器不是自己的怎么办
  • 自己做国际网站福建省文明建设办公室网站
  • 天津专业做网站的公司私人免费网站怎么下载
  • 深圳网站设计灵点网络口碑好广州海珠建网站
  • 网站开启gzip压缩西安的推广公司
  • 深圳彩票网站建设企业邮箱免费版开通
  • 佛山网站建设网络推广wordpress文章加音频
  • 设计师新手接单网站怎么把自己做的网站
  • 动漫制作专业在国企河北网络营销推广seo
  • 潜江网站建设兼职万网人网站备案流程
  • 物流官网网站今天新闻联播
  • 郑州网站开发的公司三亚今天最新通知
  • 足球直播网站怎么做东莞市公司网站建设平台
  • 建设网站需要了解什么校园网站建设和管理工作制度
  • 网站商务通弹出窗口图片更换设置移动端cpu
  • 成都画册设计的公司海外seo托管
  • 中国早期互联网公司河南seo网站开发