==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


  • 1.深入浅出之BIO

    • 1.1 简单代码示例

    • 1.2 BIO相关概念

    • 1.3 一张图理解BIO

    • 1.4 Q&A

  • 2.深入浅出之NIO
    • 2.1 简单代码示例

    • 2.2 NIO相关概念

    • 2.3 一张图理解NIO

    • 2.4 Q&A

  • 3.深入浅出之AIO
    • 3.1 简单代码示例

    • 3.2 AIO相关概念

    • 3.3 一张图理解AIO

    • 3.4 Q&A

  • 4.总结
    • 4.1 BIO、NIO和AIO优缺点

    • 4.2 一张图理解三者区别

    • 4.3 详细代码实例




1.深入浅出之BIO

JAVA的BIO可以说用起来非常简单了,相对于C/C++繁琐的创建、参数指定等,JAVA只需要几步即可。但是如果是新接触JAVA BIO的,是比较难理解,例如本菜鸟,刚接触这个的时候,虽然能照壶画瓢的写出来,但是过后马上忘记,追根究底还是封装得太好,以至于用起来用感觉有点迷糊,理解得不透彻。所以我要把这些相关概念,都给挖清楚。
注:不论JAVA如何封装,其底层要么是用windows的socket模型,要么用的是linux的socket模型,所以如果懂得了底层的原理,我们只需要去理解,JAVA为什么要这么封装,封装的手法是怎样的即可。

1.1 简单代码示例

server

public class BIOServer {public static void main(String[] args) throws IOException {//创建服务端套接字 & 绑定host:port & 监听clientServerSocket serverSocket = new ServerSocket(9999);//等待客户端连接到来Socket socket = serverSocket.accept();//拿到输入流 -- client write to serverInputStream in = socket.getInputStream();//拿到输出流 -- server write to clientOutputStream out = socket.getOutputStream();while (true){//将数据读到buf中byte[] buf = new byte[32];//server read from clientint len = in.read(buf);//如果len == 1,说明client已经断开连接if(len == -1){throw  new RuntimeException("连接已断开");}System.out.println("recv:" + new String(buf, 0, len));//将读出来的数据写回给client//如果不使用偏移量,可能会将buf中的无效数据也写回给clientout.write(buf, 0, len);}}
}

client

public static void main(String[] args) throws IOException, InterruptedException {//创建客户端套接字 & 连接服务器Socket socket = new Socket("127.0.0.1", 9999);//拿到输入流 -- server write to client, client read from serverInputStream in = socket.getInputStream();//拿到输出流 -- client write to serverOutputStream out = socket.getOutputStream();byte[] send = "hello".getBytes();while (true){//client write to serverout.write(send);byte[] buf = new byte[32];//read from serverint len = in.read(buf, 0 ,send.length);//如果len == 1,说明server已经断开连接if(len == -1){throw  new RuntimeException("连接已断开");}System.out.println("recv:" + new String(buf, 0, len));Thread.sleep(1000);}}

1.2 BIO相关概念

  • BIO
    BIO中的B是Blocking的意思,所以这是一种阻塞的IO模型
  • ServerSocket / Socket
    服务端 / 客户端套接字,其封装了SocketImpl的操作。SocketImpl是啥呢?
/*** The abstract class {@code SocketImpl} is a common superclass* of all classes that actually implement sockets. It is used to* create both client and server sockets.* <p>* A "plain" socket implements these methods exactly as* described, without attempting to go through a firewall or proxy.** @author  unascribed* @since   JDK1.0*/
public abstract class SocketImpl implements SocketOptions {/*** The actual Socket object.*/Socket socket = null;ServerSocket serverSocket = null;……
}

源码中有解释,大概意思说,这是一个公共的抽象类,所有的socket实现类,都要继承该 类。并且是用来创建server和client socket的。 那么socket都有哪几种实现类呢?
在这里插入图片描述
其中需要关注AbstractPlainSocketImpl 和 SocksSocketImpl实现,AbstractPlainSocketImpl是SocketImpl默认的实现,可以这么理解,AbstractPlainSocketImpl简单的实现了,create、accept、listen、bind、connect等操作。

/*** Default Socket Implementation. This implementation does* not implement any security checks.* Note this class should <b>NOT</b> be public.** @author  Steven B. Byrne*/
abstract class AbstractPlainSocketImpl extends SocketImpl
{
……
}

SocksSocketImpl间接继承AbstractPlainSocketImpl,SocksSocketImpl再将默认实现,封装一层。

/*** SOCKS (V4 & V5) TCP socket implementation (RFC 1928).* This is a subclass of PlainSocketImpl.* Note this class should <b>NOT</b> be public.*/class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
……
}

它对SOCKS V4&V5协议进行封装,它会先尝试是否可以用V4协议,不行就会用V5。两个协议的区别。一般情况下ServerSocket / Socket, 默认会使用SocksSocketImpl。

	/*** Sets impl to the system-default type of SocketImpl.* @since 1.4*/void setImpl() {if (factory != null) {impl = factory.createSocketImpl();checkOldImpl();} else {// No need to do a checkOldImpl() here, we know it's an up to date// SocketImpl!impl = new SocksSocketImpl();}if (impl != null)impl.setSocket(this);}

最后,我们应该都清楚ServerSocket / Socket是什么回事了吧,其实就是前者封装了服务端的操作,后者封装了客户端的操作,所以不要傻傻分不清了。

  • InputStream / OutputStream
    输入输出流,JAVA将一切IO操作都定义为流的操作,socket IO也不例外,所以ServerSocket的InputStream对应Socket的OutputStream,意思为client 写数据到OutputStream,server从InputStream中读取出来。反之亦然。
    顺便提一句,Socket使用的stream是SocketInputStream / SocketOutputStream,它们又继承了FileInputStream / FileOutputStream,因为socket也是一种文件描述符(fd)。

1.3 一张图理解BIO

在这里插入图片描述
从图中可以看出

  • 服务端
    1.使用一个线程accept客户端的请求,accept会阻塞
    2.使用N个线程处理和客户端之前的通信
    3.每个线程(socket)都拥有自己的transport逻辑。内部读写可分离成读线程和写线程。
    4.read和write都会阻塞。
  • 客户端
    1.逻辑很简单,一个线程处理读写,或者读写双线程都行。

1.4 Q&A

  • BIO只能是阻塞的吗?
    是的,ServerSocket并没有提供设置非阻塞的参数,而你又改不了底层的代码,所以只能是阻塞的。为什么会产生这样的疑问呢?因为在C/C++中,就算你不使用select模型,你照样可以用非阻塞模式。

  • write可以设置为非阻塞吗?
    答案是可以的,但是JAVA BIO不支持,所以read的时候,如果读缓冲区为空,是会阻塞在那里的,同理,write的时候,写缓冲区已满,也会阻塞再那里。

  • 如何理解Socket中的InputStream / OutputStream ?
    其实答案就在上图中,你可以理解为每个socket都有两条管道,一条可以将数据发送出去(out),另一条可以将另一端发送来的数据读出来(in)。

  • BIO真的只能一个client一个线程处理吗?
    当然了,你要是强行一个线程处理所有的事情,那肯定是没问题的,但是效率会非常之慢就是了。例如当你阻塞在read/write的时候,就会accept不到client的请求,又或者你阻塞在别的client的read的时候,其它client的read、write你都不能进行处理。说白了就是强行串行化了。

  • ……

2.深入浅出之NIO

java NIO真的比较绕,因为它不直接使用流的概念,而使用Channel。还加入了一个缓冲区Buffer的概念,所以我觉得要想弄明白NIO,首先先弄清楚什么是Buffer和Channel。其次才是selector,select底层模型其实比较简单,一句话概括就是选择可用的socket进行accept、read和write操作。

2.1 简单代码示例

server
public class NIOServer {public static void main(String[] args) throws IOException {//创建服务端socket通道 & 绑定host:portServerSocketChannel serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(9999));//设置为非阻塞模式serverSocketChannel.configureBlocking(false);//新创建一个selector(其实可以为每一个channel单独创建一个selector)Selector selector = Selector.open();//将该通道注册到该selector上,并且注明感兴趣的事件,因为是服务端通道,所以只对accept事件感兴趣serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true){//selector会帮我们去轮询,当前是否有我们感兴趣的事件发生,一直阻塞到有为止//select还有一个方法,可以指定阻塞时间,超过这个时间就会返回,此时可能返回的key个数为0selector.select();//若返回的key个数不为0,那么就可以一一处理这些事件Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()){SelectionKey selectionKey = iterator.next();//remove是为了下一次select的时候,重复处理这些已经处理过的事件//什么意思呢?其实selector.selectedKeys()返回来的set,就是其//内部操作的set,引用的是同一个set,所以我们如果不在外面remove已经//处理的事件,那么下一次,还会再次出现。需要注意的是,如果在外面对set//进行add操作,会抛异常,简单的说就是在外只删不增,在内只增不删。iterator.remove();//SelectionKey.OP_ACCEPT事件if(selectionKey.isAcceptable()){SocketChannel socketChannel = ((ServerSocketChannel) selectionKey.channel()).accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);//SelectionKey.OP_READ事件} else if(selectionKey.isReadable()){//selectionKey.channel()返回的SelectableChannel是SocketChannel的父类//所以可以直接强转SocketChannel socketChannel = (SocketChannel)selectionKey.channel();//NIO规定,必须要用Buffer进行读写ByteBuffer buffer = ByteBuffer.allocate(32);int len = socketChannel.read(buffer);if(len == -1){throw  new RuntimeException("连接已断开");}//上面那一步只是读到缓冲区,这里是从缓冲区真正的拿出数据byte[] buf = new byte[len];//这个操作可以举个例子//例如read(buffer)的时候,其实内部是调用了buffer.put这个方法//那么read结束,position的位置必定等于len//所以我们必须重置一下position为0,才可以从头开始读,但是读到什么地方呢?//那就需要设置limit = position,所以flip后,position=0, limit = lenbuffer.flip();buffer.get(buf);System.out.println("recv:" + new String(buf, 0, len));//注册写事件selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE);//SelectionKey.OP_WRITE事件} else if(selectionKey.isWritable()){SocketChannel socketChannel = (SocketChannel)selectionKey.channel();//写数据,也要用Buffer来写int len = socketChannel.write(ByteBuffer.wrap("hello".getBytes()));if(len == -1){throw  new RuntimeException("连接已断开");}//这里为什么要取消写事件呢?因为只要底层的写缓冲区不满,就会一直收到这个事件//所以只有想写数据的时候,才要注册这个写事件selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);}}}}
}

client

public class NIOClient {public static void main(String[] args) throws IOException, InterruptedException {//创建客户端socket通道 & 连接host:portSocketChannel socketChannel = SocketChannel.open();//设置为非阻塞模式socketChannel.configureBlocking(false);//非阻塞的形式连接服务器,如果直接使用open带参数的,连接的时候是阻塞连接socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));//新创建一个selectorSelector selector = Selector.open();//将该通道注册到该selector上,并且注明感兴趣的事件socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);while (true){selector.select();Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()){SelectionKey selectionKey = iterator.next();iterator.remove();//连接事件if(selectionKey.isConnectable()){//看源码的注释可以知道,如果不使用带参数的open,那么需要手动调用这个方法完成连接//如果是阻塞模式,该方法会阻塞到连接成功,非阻塞模式下,会立刻返回,已连接true,未连接falseif(socketChannel.finishConnect()){//需要取消连接事件,否则会一直触发该事件,注册写事件selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE);}} else if(selectionKey.isReadable()){ByteBuffer buffer = ByteBuffer.allocate(32);int len = socketChannel.read(buffer);if(len == -1){throw  new RuntimeException("连接已断开");}byte[] buf = new byte[len];buffer.flip();buffer.get(buf);System.out.println("recv:" + new String(buf, 0, len));selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE);} else if(selectionKey.isWritable()){int len = socketChannel.write(ByteBuffer.wrap("hello".getBytes()));if(len == -1){throw  new RuntimeException("连接已断开");}selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);//这个只是控制一下发送数据的速度Thread.sleep(1000);}}}}
}

2.2 NIO相关概念

  • Buffer概念
    缓冲区,它的内存分配有两种实现,第一种是jvm堆内存分配缓冲区大小,第二种是直接内存分配缓冲区大小。这两种的详细区别,这里不好展开讲,简单说呢,使用jvm堆内存做缓冲区,易于垃圾回收,速度比直接内存更快,但是将数据拷贝到内核空间却需要两次,第一次是拷贝到对外内存,对外内存再到内核空间。如图
    在这里插入图片描述
    然后,我们来讲本质上,Buffer是这个什么东西,其实它就是一个数组,然后给你提供各种骚操作,仅此而已。
  • Buffer重要的几个参数
    直接来个读写的例子
//分配32个字节大小的空间
ByteBuffer buffer = ByteBuffer.allocate(32);
//需要写入的字节数组
byte[] writeBuf = "hello".getBytes();
//调用put将数据写入缓冲区
buffer.put(writeBuf);
//重置position和limit,为下一次的读做准备
buffer.flip();
byte[] readBuf = new byte[5];
//将缓冲区的数据读到readBuf数组中
buffer.get(readBuf);
System.out.println(new String(readBuf));

接下来,我们来断点调试看看Buffer中参数的变化
首先,分配大小后
在这里插入图片描述
看,IDE都给你把重点参数显眼的放在第一行了。
1.hb就是Buffer中的那个数组,就是那个高大上的缓冲区
2.position就是当前可操作的位置(例如开始从position读,开始从position写)
3.limit就是可操作的限定范围(例如读的时候,你不能读超过limit后面的数据,写的时候,你不能写超过limit后的数据)。一般初始化时limit=cap
4.cap就是Buffer(hb)的大小

接下来将数据写入缓冲区
在这里插入图片描述
可以看到,只有position变了,因为写入了5个字节,那么下一次写入的位置肯定是hb[5]的位置,所以position=5

让我们想一想,如果下一次读的时候,我们直接从hb[5]开始读吗?显示不会,这样根本就读不到东西,所以就有了flip这个方法,看一下它的源码

public final Buffer flip() {limit = position; //将limit限制到5这个位置,相当于写的末尾position = 0;    //将position重新置为0,相当于写的开始mark = -1;return this;
}

调用这个方法后,读的时候,就会从写的开始读到写的末尾,是不是正好把写进去的数据读出来了?只要弄清楚了这三个参数的意义,其它参数不过是在这三个参数上锦上添花。
在这里插入图片描述
最后读取数据
在这里插入图片描述
到这里可能会有人疑惑,那如果我要再次写入呢?现在limit=position了,写不进去了。没错,所以还需要一个重新置位的方法clear
将代码修改后

ByteBuffer buffer = ByteBuffer.allocate(32);
while (true){byte[] writeBuf = "hello".getBytes();buffer.put(writeBuf);buffer.flip();byte[] readBuf = new byte[5];buffer.get(readBuf);buffer.clear();System.out.println(new String(readBuf));
}

来看一下clear的源码

public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;
}

看,是不是回到了最初的样子~

  • chanel概念
    chanel和inputstream / outputstream的区别是,前者是双向的,后者是单向的。也就是说,只要你创建一个chanel,你可以用这个chanel进行读写操作。并且chanel是基于Buffer来操作的,不管是读还是写都需要通过Buffer这个东东。画个图,简单理解一下。
    在这里插入图片描述
    图中很直观的表现出A和B的chanel都是通过Buffer来读写数据的。上图虽然只画了一个Buffer,但是不止可以有一个Buffer,你可以创建一个读Buffer一个写Buffer都是可以的。
    chanel的读写方法,请看定义
public abstract int read(ByteBuffer buffer)
public abstract int write(ByteBuffer buffer)
  • chanel的两种实现对比
    FileChannel和SocketChannel,读写的实现的区别FileChannel的读写继承至SeekableByteChannel,SocketChannel继承至ReadableByteChannel,如图
    在这里插入图片描述
    在这里插入图片描述
    很明显,从名字就可以看出,SeekableByteChannel支持记录文件的当前位置,所以当使用
    FileChannel读写文件的时候,不仅需要注意文件position的值,还需要Buffer position的值。这里来一个FileChannel读写文件的小例子。
public static void main(String[] args) throws IOException, InterruptedException {//创建文件通道FileChannel fileChannel = FileChannel.open(new File("a.txt").toPath(), new StandardOpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.READ});//读缓冲区ByteBuffer readBuffer = ByteBuffer.allocate(32);int i = 0;while (true){//记录写入的字节数int wLen = fileChannel.write(ByteBuffer.wrap(("" + ++i) .getBytes()));//将文件的位置重置为写之前的位置,为下次读做准备//这里是需要特别注意的,如果没有这一步,你写是写成功了,但是读永远不会成功,因为此时position永远在末尾fileChannel.position(fileChannel.position() - wLen);//文件数据读到buffer中,读取完后,position的位置又回到上一步写结束的位置了int rLen = fileChannel.read(readBuffer);byte[] buf = new byte[rLen];//Buffer的读操作readBuffer.flip();readBuffer.get(buf);readBuffer.clear();System.out.println(new String(buf));Thread.sleep(1000);}}

SocketChannel的话,就只需要关注Buffer就行,比较简单。FileChannel不能设置非阻塞,并且看实现,也不能使用selector。

  • selector
    选择器,这个是最好懂的了,如果不明白的话,可以去百度搜select模型,如果是熟悉windows的,可以看我之前写过的文章select模型服务器设计,如果是熟悉linux的,已经有很多人分析过select模型了。

2.3 一张图理解NIO

在这里插入图片描述
从图中可以看到,我们不再主动的去请求内核,而是让它有主动通知我们。
最终,我们都是在读处理和写处理中,用channel发送/接收数据。

2.4 Q&A

  • Channel和Buffer的关系?
    可以这么类比
    channel.read(buffer)
    buffer.read(buf)
    相当于
    in.read(buf)
    抛开stream和channel的差别,其实只是多了一个缓冲区。如图
    在这里插入图片描述

  • 使用Buffer的好处?
    肯定很多人在写demo的时候,觉得Buffer并没有什么用啊?
    因为可能demo一般都这么写
    ByteBuffer buffer = ByteBuffer.allocate(32);
    ……
    channel.read(buffer);
    ……
    byte[] buf = new byte[32];
    buffer.read(buf);
    瞧,这样不是脱了裤子放屁吗?为什么不直接内核copy到我的buf呢?没错,如果是这样写,确实太鸡肋了,但是这样写,就可以体现出好处了。
    ByteBuffer buffer = ByteBuffer.allocate(1024 * 4);
    ……
    channel.read(buffer);
    ……
    while(true){
    byte[] buf = new byte[32];
    buffer.read(buf);
    //handle
    }
    如果有1024 * 4个字节,那么没有缓冲区,需要1024 * 4 / 32 次IO操作,
    现在有了缓冲区,那么只需要一次IO操作,其它操作都在内存中进行,是不是高效了很多呢?

  • write事件有什么用?
    我们假设如果不支持write异步,那么是不是会发生BIO的write阻塞的问题?
    所以,这个write事件就是,当可以写的时候,我会通知你,你不需要阻塞在那里等着。
    可能有人会疑问,那我想写的内容,如何异步的时候写呢?SelectionKey有这么一个方法attach(Object ob)方法,可以附加参数,所以,当你想异步写的时候,先把写的内容放进去,等收到可写消息,再拿出来写即可。

  • connect事件有什么用?
    因为connect都是客户端发起,一般只会发起一次,直接阻塞调用不就好了?
    确实是可以的,但是为了解耦发起连接和处理连接这两个事情,所以需要异步connect。

  • 为什么我在别的线程增加感兴趣的事件不生效?
    准确的说,可能会生效,这是为什么呢?因为select会阻塞,当select阻塞的时候,你再
    增加例如写事件,那么此时它是不会自动感知到的,必须得等下一次才能拿到最新的感兴趣事件。这时候就需要一个方法了,wakeup,可以直接唤醒select,这样下一次select的时候,就能拿到你新增感兴趣的事件了。

3.深入浅出之AIO

如果理解了NIO,那么AIO其实就不难。NIO是通知你,有可用事件,需要你去处理一下,读写还是得你自己搞定。AIO也是通知你,但是,它是通知你,已经读完/写完了,你只需要从Buffer中取数就行了,真正意义上的异步。NIO为什么不是真正意义上的异步呢?因为,当收到读事件,我们需要主动发起一次IO操作,这是同步进行的。

3.1 简单代码示例

server

public static void main(String[] args) throws IOException, InterruptedException {//创建异步服务端通道 & 绑定host:portAsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(9999));//异步accept客户端请求,第一个参数是附加参数,透传进去的,可以为null//第二个参数是异步处理,回调的时候会调用里面的方法serverSocketChannel.accept(serverSocketChannel, new AcceptHandle());//main线程阻塞while (true){Thread.sleep(10000);}}//accept事件处理static class AcceptHandle implements CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel>{//当有客户端请求进来,会自动回调这个方法@Overridepublic void completed(AsynchronousSocketChannel socketChannel, AsynchronousServerSocketChannel serverSocketChannel) {//异步处理读事件,其中第一个参数是Buffer,用来存放读取的数据//第二个参数是附加参数,一直透传,啥也不干//第三个参数是异步处理读结果的类,当读成功或者失败的时候,会回调里面的方法ChannelInfo channelInfo = new ChannelInfo(socketChannel);socketChannel.read(channelInfo.getReadBuffer(), channelInfo, new ReadHandle());serverSocketChannel.accept(serverSocketChannel, this);}@Overridepublic void failed(Throwable throwable, AsynchronousServerSocketChannel serverSocketChannel) {throwable.printStackTrace();}}//read事件处理static class ReadHandle implements CompletionHandler<Integer, ChannelInfo>{//当读成功时会回调(只要不报错,都算是成功)@Overridepublic void completed(Integer integer, ChannelInfo channelInfo) {//读取长度大于0再处理if(integer > 0){//channel Buffer正常操作ByteBuffer buffer = channelInfo.getReadBuffer();buffer.flip();byte[] buf = new byte[buffer.remaining()];buffer.get(buf);buffer.clear();System.out.println("recv:" + new String(buf));//异步写,参数和read参数一模一样channelInfo.getWriteBuffer().put(buf);channelInfo.getWriteBuffer().flip();channelInfo.getSocketChannel().write(channelInfo.getWriteBuffer(), channelInfo, new WriteHandle());}//这一步一定要有,否则就只能接收到一次//其实说白了,你想异步读数据,那么就传一个人家规定好的回调方法,完成的时候,让他自动的去调用你这个方法//所以,每读一次,都需要传一次channelInfo.getSocketChannel().read(channelInfo.getReadBuffer(), channelInfo, this);}@Overridepublic void failed(Throwable throwable, ChannelInfo channelInfo) {throwable.printStackTrace();}}//write时间处理static class WriteHandle implements CompletionHandler<Integer, ChannelInfo>{//写完成后,会调用这个方法@Overridepublic void completed(Integer integer, ChannelInfo channelInfo) {System.out.println("write bytes:" + integer);channelInfo.getWriteBuffer().clear();}@Overridepublic void failed(Throwable throwable, ChannelInfo channelInfo) {throwable.printStackTrace();}}@Getter@Setterstatic class ChannelInfo{AsynchronousSocketChannel socketChannel;ByteBuffer readBuffer;ByteBuffer writeBuffer;public ChannelInfo(AsynchronousSocketChannel socketChannel){this.socketChannel = socketChannel;this.readBuffer = ByteBuffer.allocate(32);this.writeBuffer = ByteBuffer.allocate(32);}}

client

public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {//创建客户端通道AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();//异步连接,使用Future.get等待连接成功后返回//当然也可以使用方法回调,这里简单演示,就不用了socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999)).get();ByteBuffer readBuffer = ByteBuffer.allocate(32);ByteBuffer writeBuffer = ByteBuffer.allocate(32);while (true){//正常buffer写操作writeBuffer.put("hello".getBytes());writeBuffer.flip();//异步写,使用Future.get等待写成功socketChannel.write(writeBuffer).get();writeBuffer.clear();//异步读,使用Future.get等待读成功int len = socketChannel.read(readBuffer).get();byte[] buf = new byte[len];readBuffer.flip();readBuffer.get(buf);readBuffer.clear();System.out.println("recv:" + new String(buf));Thread.sleep(1000);}}

3.2 AIO相关概念

AIO的概念NIO的基本差不多,我觉得只有一个需要讲一下。那就是CompletionHandler
这是个什么东东呢?来看一下它的接口定义

public interface CompletionHandler<V,A> {/*** Invoked when an operation has completed.** @param   result*          The result of the I/O operation.* @param   attachment*          The object attached to the I/O operation when it was initiated.*/void completed(V result, A attachment);/*** Invoked when an operation fails.** @param   exc*          The exception to indicate why the I/O operation failed* @param   attachment*          The object attached to the I/O operation when it was initiated.*/void failed(Throwable exc, A attachment);
}

看出来了吧,只要你实现了这个接口,然后将它当做参数传进去。那么,当它完成读/写的时候会回调completed这个方法,同理报错的时候,会回调failed这个方法。当你深入源码中,会发现,确实有这么一段代码调用了这两个方法。
在这里插入图片描述
其中的attachment是附加参数,一直透传,不会被改变。
所以,总结一下就是,你异步的发起read操作,将Buffer和CompletionHandler当做参数传进去即可,其中Buffer是用来缓存从内核读取到的数据的,CompletionHandler里的方法是读取完成后会被调用的,一目了然了就。

3.3 一张图理解AIO

在这里插入图片描述
这个图较之NIO的,从下往上更加明显,给人的感觉就像是Server和Client就是通过Buffer来R/W一样。其实异步的目的就是让我们更专注于这一部分而已。其它都交给系统帮你完成,完全的解耦开来。

3.4 Q&A

  • 为什么在completed方法中,需要再一次调用accept/read?
    其实底层有两种做法,第一种就是这种,你每调一次read,传一个handle进去,
    处理完了,它回调handle中的方法。第二种是你只需要调一次read,传一个handle进去
    底层会有一个死循环,不停的read,然后不停的调用handle中是方法。如果优化得好,第二种未尝不可,但是缺少可定制化的效果吧估计是。使用第一种,你可以想什么时候read就
    什么时候read,但是使用第二种你就会不停的被调用,你会被迫的去处理它。

  • 为什么根据网上很多人的写的AIO demo都只能发送/接收一次数据?
    答案就是上面第一点,没有再次调用read/write。就不能再次接收/发送。

  • AIO中的接口一般都有附加参数,这是为什么?
    你想想,异步操作,当你提交一个read请求,不是立即顺序处理的,而是在另一个地方处理,那是不是会碰到你想用的某个参数,在另一个地方用不到?所以提交一个异步请求的时候就需要一个附加参数,一直透传,等它回调你的处理方法的时候,你再拿出来,就可以使用到这个参数了。

  • AIO性能会比NIO更好吗?
    理论来说是的,因为异步I/O是一个底层支持的操作,你想想,中断是不是比你轮询有效率得多。但是却几乎没有任何框架用AIO这种模型,为什么呢?因为java的AIO,甚至linux的AIO都有一点猫腻,嘿嘿,这个下回分解,这里不再展开了。

4.总结

最后总结一下使用上的流程。

  • BIO
    server端:
    1.创建ServerSocket(有的构造函数已经包含了bind)
    2.accept等待client连接
    3.使用accept返回的Socket中的InputStream/OutputStream进行通信

    client端:
    1.创建Socket(有的构造函数已经包含了connect)
    2.使用Socket中的InputStream/OutputStream进行通信

  • NIO
    server端:
    1.创建ServerSocketChannel
    2.使用ServerSocketChannel中的ServerSocket绑定host:port
    3.设置非阻塞模式
    4.创建Selector
    5.将ServerSocketChannel注册到selector中(这个清楚,别理解反拉)
    6.select阻塞
    7.遍历就绪的事件
    8.一一处理就绪事件

    Client端:
    1.创建SocketChannel
    2.设置非阻塞模式
    3.连接server
    4.创建Selector
    5.将SocketChannel注册到selector中(这个清楚,别理解反拉)
    6.select阻塞
    7.遍历就绪的事件
    8.一一处理就绪事件

  • AIO
    server端:
    1.创建AsynchronousServerSocketChannel
    2.异步调用accept
    3.AcceptHandle里异步调用read(这一步不是必须的,demo常规操作而已)
    4.ReadHandle处理读完成的数据
    5.WriteHandle确认写成功

    client端:
    1.创建AsynchronousSocketChannel
    2.异步调用connect
    3.ConnectHandle里异步调用read(这一步不是必须的,demo常规操作而已)
    4.ReadHandle处理读完成的数据
    5.WriteHandle确认写成功

4.1 BIO、NIO和AIO优缺点

不想照抄网上了,就大家自己多多思考了。

4.2 一张图理解三者区别

这个图相信大家都见过,这里只是为了加深印象,画一下。
在这里插入图片描述

4.3 详细代码实例

==> CODE

后续还会围绕socket进行一系列的文章深入探讨。

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

相关文章

  1. MapInfo格式到ArcGIS格式的转换方法

    MapInfo中的地图可以有两种格式:Tab格式(表格式)、Mif格式(交换格式)。 ArcInfo中的地图也支持多种格式:Shape格式、Coverage、E00(交换格式)、Coverage.... 由Mif->Shape:使用MapInfo工具中的通用转换器;或则使用ArcToolbox直接转换 由Mif->E00:在MapInfo中导…...

    2024/4/28 2:08:03
  2. 开源流程图工具 draw.io 的搭建

    参考 开源流程图工具 draw.io 的搭建...

    2024/4/28 4:11:51
  3. Neo4j在Windows下安装及环境配置

    由于本人研究方向为知识图谱,存储知识图谱数据用的是Neo4j图数据库,因此今天来写一下如何安装Neo4j图数据库、如何进行环境配置及服务的安装。 1. 软件下载 需要的软件有:Neo4j 官网下载目前有解压版、桌面安装版本。建议下载解压版。 JDK(百度搜索JDK安装包下载)2.安装 …...

    2024/4/28 19:56:37
  4. promise、async/await在任务队列中的执行顺序

    我们这篇主要讲浏览器和Nodejs环境下任务队列的执行顺序,尤其关注promise和async/await在任务队列中的执行顺序。 1.setTimeout、setImmediate、nextTick、Promise.next情况下 我们先记住几条结论: 1.有process和setImmediate就考虑是在nodejs环境下 2.微任务中nextTick队列在…...

    2024/4/28 9:39:47
  5. java aio tomcat bio nio apr 模式性能测试

    11.11活动当天,服务器负载过大,导致部分页面出现了不可访问的状态、那后来主管就要求调优了,下面是tomcat bio、nio、apr模式以及后来自己测试的一些性能结果。 原理方面的资料都是从网上找的,并且把多个地方的整理到了一起,觉得很有意义。(后面对tomcat默认页面测试的数…...

    2024/4/27 23:23:02
  6. 使用移位操作符实现奇偶数的判断,C和Java版1

    C语言版: #include <stdio.h> int fun_isodd(int x){while(x!=-1){if(((x>>1)<<1) == x){return 0; //是偶数}return 1; //是奇数}return 1; //是奇数 } void main(){int m;printf("请输入一个整数:");scanf("%d",&…...

    2024/4/28 2:40:55
  7. Java中网络IO的实现方式-BIO、NIO、AIO

    在网络编程中,接触到最多的就是利用Socket进行网络通信开发。在Java中主要是以下三种实现方式BIO、NIO、AIO。关于这三个概念的辨析以前一直都是好像懂,但是表达的不是很清楚,下面做个总结完全辨析清楚。1. BIO方式首先我用一个较为通俗的语言来说明:BIO 就是阻塞IO,每个T…...

    2024/4/28 7:30:59
  8. 微信小程序中 async/await 的使用

    async起什么作用?async函数返回的是一个Promise对象。Async函数(包含函数语句、函数表达式、Lambda表达式)会返回一个Promise对象,如果在函数中return一个直接量,async会把这个直接量通过Promise.resolve()封装成Promise对象。async函数返回的是一个Promise对象,所以在最外…...

    2024/4/28 14:01:50
  9. neo4j备份/恢复(导入导出)及查询

    参考文章:neo4j-备份、恢复Neo4j(v1.8与v1.9) HA高可用性、备份还原NEO4J亿级数据导入导出以及数据更新NEO4J亿级数据导入导出以及数据更新关于neo4j亿级数据提高查询效率的方法总结Neo4j Desktop导入CSV数据文件大规模数据导入Neo4j图数据库Neo4j搭建以及大批量数据导入使用n…...

    2024/4/19 12:05:46
  10. 推荐优秀的开源GIS软件

    推荐优秀的开源GIS软件(以后会补充)(2011-11-14 13:17:55) 转载▼标签: it分类: 读书笔记推荐优秀的开源GIS软件(以后会补充)从GIS入门到现在,我已经接触不少优秀的GIS软件,这里列出我使用过优秀的开源GIS软件。桌面GIS软件:Qgis(基于Qt使用C++开发的跨平台桌面软件,…...

    2024/4/28 6:24:40
  11. MapInfo Callback的Delphi实现

    ---- 地理信息系统软件MapInfo在各行各业都有广泛的应用。由于本行业的需要,我们往往要对MapInfo进行二次开发。很多情况下是采用将MapInfo作为OLE对象集成到二次开发的应用程序中。开发的客户应用程序作为客户端运行在前台,直接与用户进行交互操作;MapInfo作为服务器在后台…...

    2024/4/28 6:07:03
  12. Neo4j在线(全量/增量)和离线备份教程(含单机和集群恢复教程)

    本文根据Neo4j3.5官方文档编写。主要介绍离线备份、单机/集群恢复,在线全量/增量备份、单机/集群恢复。说在前头,Neo4j社区版仅支持离线备份,企业版才支持在线备份。我用的社区版是3.5.8,企业版是3.5.7,默认数据为graph.db一、离线备份与恢复主要是用 neo4j-admin dump 和…...

    2024/4/28 4:06:03
  13. Java NIO与BIO

    1,NIO与BIO的区别BIO:传统的同步阻塞模型BIO是通过Socket和ServerSocket实现的,ServerSocket监听端口,Socket进行连接。这种情况不适合处理多个请求:1,生成较多的Socket会消耗过多的本地资源,2,Socket连接的速度比较慢,3,BIO一般都是采取accpet获取Socket后,给一个请…...

    2024/4/28 4:17:35
  14. 小程序中使用async await的注意点

    需要在文件引入regenerator-runtime (注:也就是facebook的regenerator, 下载本地引入即可)import regeneratorRuntime from ../../libs/regenerator-runtime;...

    2024/4/24 16:22:46
  15. 利用了移位操作,取第几位的值(二进制)

    public static void main(String[] args) {Integer dbCLProgress =10;Integer j=4;// 利用了移位操作,取第几位的值 先进行右移n-1,与1进行&运算Integer status=(dbCLProgress >> (j-1)) & 1;System.out.println(status);}...

    2024/4/28 8:25:45
  16. BIO,NIO,AIO的区别有哪些?

    要理解BIO,NIO,AIO,首先要理解同步和异步,阻塞和非阻塞这两对概念。 举个例子,去银行存钱,将钱和存折都交给了窗口的服务人员,然后你要一直坐在那里等柜员帮你处理好业务,最后告诉你存好了把存折还给你,你再离开,这是同步。 如果刚好这个柜员是你的发小,你跟他说:“我…...

    2024/4/28 16:42:52
  17. Async函数:await后的代码段由哪个线程执行?

    阅读本篇文章前需要C#多线程基础、Async函数基础、SynchronizationContext基础默认情况以下是测试代码:static void Main(string[] args){Test0();Console.Read();}static void Test0(){AsyncMethod();Console.WriteLine("after calling AsyncMethod : " + Thread.C…...

    2024/4/28 0:29:17
  18. C语言位运算实现循环移位

    总长度N 数据a 循环左移n位 (a>>(N-n))|(a>>n) 循环右移n位 (a<<(N-n))|(a>>n)...

    2024/4/28 17:35:17
  19. neo4j数据导入导出

    执行数据导出命令./neo4j-admin dump --database=graph.db --to=/soft/graph.db.dump 执行数据导入命令neo4j-admin load --from=/soft/graph.db.dump --database=graph.db --force...

    2024/4/24 16:22:42
  20. MapInfo系列产品介绍

    MapInfo系列产品介绍 除MapInfo Professional外,MapInfo系列产品有: (1)MapBasic MapBasic是MapInfo的二次开发语言,是MapInfo应用开发环境。用于扩展MapInfo功能、实现自动化操作和进行软件系统的集成等。它和MapInfo Professional组成了MapInfo的技术核心。 (2)MapIn…...

    2024/4/28 3:10:03

最新文章

  1. 必应bing广告推广开户时间需要多久?

    企业选择合适的平台进行广告投放成为了企业获取竞争优势的关键一步&#xff0c;必应Bing作为全球第二大搜索引擎&#xff0c;凭借其庞大的用户基础和精准的广告定位能力&#xff0c;成为了众多企业海外及国内市场推广的优选渠道。云衔科技以专业、高效的服务&#xff0c;成为企…...

    2024/4/28 21:38:20
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Redis分区

    Redis分区是一种数据分片技术&#xff0c;用于将数据分布到多个Redis实例&#xff08;节点&#xff09;上以提高性能和扩展性。分区使得Redis能够处理比单个实例更大的数据集&#xff0c;并允许并行处理客户端请求。 原理&#xff1a; Redis分区通过一致性哈希算法&#xff08;…...

    2024/4/23 6:11:42
  4. 【Linux实验室】NFS、DHCP的搭建

    NFS、DHCP的搭建 1、nfs服务搭建及测试什么是NFS&#xff1f;环境准备服务端机器安装nfs-utils和rpcbind包启动NFS服务创建/data/NFSdata目录&#xff0c;配置nfs文件启动服务挂载测试在服务端在共享目录下创建文件测试在客户端在共享目录下创建文件 2、dhcp服务搭建及测试什么…...

    2024/4/23 12:03:02
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/28 13:52:11
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/28 3:28:32
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/28 13:51:37
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/28 15:57:13
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/28 1:22:35
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57