NIO基础
一、NIO基础
Java New IO是从Java1.4版本开始引入的一个新的IO api,可以替代以往的标准IO,NIO相比原来的IO有同样的作用和目的,但是使用的方式完全不一样,NIO是面向缓冲区的,基于通道的IO操作,这也让它比传统IO有着更为高效的读写。
1.1 IO和NIO的主要区别
IO | NIO |
---|---|
面向流 | 面向缓冲 |
区阻塞IO | 非阻塞IO |
无 | 选择器 |
1.1.1 传统IO的流
以下用图来简单理解一下,在传统IO中当App要对网络,磁盘中的文件进行读写的时候,它们必须建立一个连接,流到底是一个什么样的概念呢,我们可以先把它想象成自来水,家里要用自来水,需要有水管,让水从水管过来到家里,起到一个运输的作用。
所以当我们文件中的数据需要输入到App里面时,它们就会建立一个输入的管道。而当我们的App有数据需要写入到文件系统的时候,就会建立一个输出的管道,这两条管道就是我们的输入流和输出流。那水从来没有逆流而上的呀,所以它们都是单向管道。这么一讲,是不是就很好懂了呢😁?
1.1.2 NIO
也是同样的文件系统和App,不过此时把流换成了一个channel,现在我们可以先认为它就是一条铁道,那我们知道铁道本身是不能传递货物的呀,所以我们需要一个载具—火车(也就是缓冲区),App需要的数据就由这个名叫缓冲区的载具运输过来。那火车是可以开过来,也可以开回去的,所以NIO是双向传输的。
1.2 Buffer
NIO的核心在于,通道(channel)和缓冲区(buffer)两个。通道是打开到IO设备的连接。使用时需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区,然后通过操作缓冲区对数据进行处理。(其实就是上面那张图的事儿,或者一句话就是一个负责传输,一个负责存储)。
缓冲区是Java.nio包定义好的,所有缓冲区都是Buffer抽象类的子类。Buffer根据数据类型不同,常用子类分别是基本数据类型除了Boolean外的xxxBuffer(IntBuffer,DoubleBuffer···等)。不同的Buffer类它们的管理方式都是相同的,获取对象的方法都是
// 创建一个容量为capacity的xxx类型的Buffer对象
static xxxBuffer allocate(int capacity)
而且缓冲区提供了两个核心方法:get()和put(),put方法是将数据存入到缓冲区,而get是获取缓冲区的数据。
此时我们用代码看一下
public class BufferTest {@Testpublic void testBuffer(){// 创建缓冲区对象ByteBuffer byteBuffer = ByteBuffer.allocate(1024);}
}
点进去ByteBuffer,会看到这个东西是继承了Buffer类的
public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>
此时继续点进去Buffer类,第一眼看到的是有几个自带的属性
1.2.1 buffer的基本属性
① capacity容量
表示Buffer的最大数据容量,这个值不能为负。而且创建后是不能更改的。
② limit限制
第一个不能读取或写入的数据的索引,位于此索引后的数据不可读写。这个数值不能为负且不能超过capacity,如上图中第三个缓冲区,在下标为5之后的数据块均不能读写,那limit为5
③ position位置
下一个要读取或写入的数据的索引,这个数值不能为负且不能超过capacity,如图中第二个缓冲区,前面5块写完成,此时第6个数据块的下标为5,所以position为5
④ mark标记/reset重置
mark是一个索引,通过Buffer的mark()方法指定Buffer中一个特定的position后,可以通过reset()方法重置到这个position,这个通过代码来解释会比较好说明
1.2.2 code部分(非常简单)
1.首先我们创建一个缓冲区对象,然后把它的属性打印出来
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.limit());运行结果:0,10,10
2.执行一个put()方法,来把一个字符丢进去
String str = "abcde";
byteBuffer.put(str.getBytes());
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.limit());运行结果:5,10,10
"abcde"长度为5,position已经变化,其它不变
3.使用flip()切换为读模式
byteBuffer.flip();
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.limit());运行结果:0,10,5
此时position变成为0了,因为一开始的5,是因为这时候要写的是下标为5的数据块,而转换成读模式后,第一个读的明显是下标为0的数据块呀。limit的数值也变成了5,因为当前能读到的数据从下标为5开始就木有了,所以limit为5
4.简单获取一下buffer中的数据
byte[] array = new byte[byteBuffer.limit()];
byteBuffer.get(array);
System.out.println(new String(array,0,array.length));运行结果:abcde
5.mark() & reset()
byte[] array = new byte[byteBuffer.limit()];
byteBuffer.get(array,0,2);
System.out.println(new String(array,0,2));
System.out.println(byteBuffer.position());byteBuffer.mark();
byteBuffer.get(array,2,2);
System.out.println(new String(array,2,2));
System.out.println(byteBuffer.position());byteBuffer.reset();
System.out.println(byteBuffer.position());运行结果:ab,2,cd,4,2
其实很简单,就是第一次读取的时候,只是读取了前面两个字符,然后此时position的结果为2,然后再读取后两个,position为4,可是因为我在读取前面2个的时候进行了一个mark操作,它就自动回到我mark之前的那个读取位置而已,就是这么简单
6.其他的一些方法
rewind()方法,可重复读,clear()清空缓冲区,不过这个方法的清空缓冲区,是一种被遗忘的状态,就是说,数据仍然还存于缓冲区中,可是自动忽略掉了。此时再次读取数据,是还是可以get()到的。hasRemaining()方法就是表示剩余可操作的数据量还有多少,比如刚刚的mark的那个例子中,我reset回去之后,剩余的可操作数据就是3,因为我只读了ab,还有cde这三个。
1.2.3 直接缓冲区和非直接缓冲区
非直接缓冲区:通过allocate()方法来分配缓冲区。将缓冲区建立在JVM的内存中。
直接缓冲区:通过allocateDirect()方法分配缓冲区,将缓冲区建立在物理内存中。效率更高。
① 非直接缓冲区
应用程序想要在磁盘中读取数据时,首先它发起请求,让物理磁盘先把它的数据读到内核地址空间当中,之后这个内核空间再将这个数据copy一份到用户地址空间去。然后数据才能通过read()方法将数据返回个应用程序。而应用程序需要写数据进去,也是同理,先写到用户地址空间,然后copy到内核地址空间,再写入磁盘。此时不难发现,这个copy的操作显得十分的多余,所以非直接缓冲区的效率相对来说会低一些。
② 直接缓冲区
直接缓冲区就真的顾名思义非常直接了,写入的时候,写到物理内存映射文件中,再由它写入物理磁盘,读取也是磁盘把数据读到这个文件然后再由它读取到应用程序中即可。没有了copy的中间过程。
1.3 channel
1.3.1 扯一下概念背景
由java.nio.channels包定义,表示IO源与目标打开的链接,它本身不存在直接访问数据的能力,只能和Buffer进行交互
传统的IO由cpu来全权负责,此时这个设计在有大量文件读取操作时,CPU的利用率会被拉的非常低,因为IO操作把CPU的资源都抢占了。
在这种背景下进行了一些优化,把对cpu的连接取消,转为DMA(直接内存存取)的方式。当然DMA这个操作本身也是需要CPU进行调度的。不过这个损耗自然就会比大量的IO要小的多。
此时,就出现了通道这个概念,它是一个完全独立的处理器。专门用来负责文件的IO操作。
1.3.2 常用通道
Java为Channel接口提供的主要实现类:
FileChannel:用于读取,写入,映射和操作文件的通道
DatagramChannel:通过UDP读写网络中的数据通道
SocketChannel:通过TCP读写网络中的数据通道
ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel
获取channel的一种方式是对支持通道的对象调用getChannel()方法,支持类如下
FileInputStream
FileOutputStream
RandomAccessFile
DatagramSocket
Socket
ServerSocket
获取的其他方式是使用Files类的静态方法newByteChannel()获取字节通道。再或者是通过通道的静态方法open()打开并返回指定通道。
1.3.3 常用方法和简单使用
① 使用非直接缓冲区完成文件复制
// 创建输入输出流对象
FileInputStream fileInputStream = new FileInputStream("testPic.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("testPic2.jpg");// 通过流对象获取通道channel
FileChannel inChannel = fileInputStream.getChannel();
FileChannel outChannel = fileOutputStream.getChannel();// 创建指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 将通道中的数据写入到缓冲区中
while (inChannel.read(byteBuffer) != -1){// 切换成读取模式byteBuffer.flip();// 将缓冲区中的数据写到输出通道outChannel.write(byteBuffer);// 清空缓冲区byteBuffer.clear();}
//回收资源(这里为了省时间直接抛出去了,反正这段不太重要)
outChannel.close();
inChannel.close();
fileInputStream.close();
fileOutputStream.close();运行结果:就自然是复制了一个testPic2出来啦
因为代码本身不难,注释已经写得比较详细,就不展开了
② 使用直接缓冲区来完成文件的复制
注意这里的StandardOpenOption是一个枚举,表示模式,很显然这里是要选择READ读取模式。
FileChannel inChannel = FileChannel.open(Paths.get("testPic.jpg",StandardOpenOption.READ));
FileChannel outChannel = FileChannel.open(Paths.get("testPic2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
// 进行内存映射
MappedByteBuffer inMappedBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMapBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());// 对缓冲区进行数据的读写操作
byte[] array = new byte[inMappedBuffer.limit()];
inMappedBuffer.get(array);
outMapBuffer.put(array);// 回收资源
inChannel.close();
outChannel.close();
如果需要看一下它们两个的时间差,自己用最常规的系统时间来瞧瞧就好,在这里就不再加上了。
二、NIO非阻塞式网络通信
传统的IO流都是阻塞式的,当一个线程调用read或者write时,该线程被阻塞,直到数据被读取或者写入,该线程在此期间都是不能执行其他任务的,因此,在完成网络通信进行IO操作时,线程被阻塞,所以服务器端必须为每个客户端提供一个独立线程进行处理,当服务器端需要处理大量客户端时,性能将会急剧下降。
NIO是非阻塞的,当线程从某通道进行读写数据时,若没有数据可用,该线程可以进行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道。因此NIO可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。
2.1 Selector
这个选择器其实就是在客户端和服务端之间引入一个通道的注册器,比如现在我的客户端要像服务端传输数据了,客户端会给选择器去发送一个channel的注册请求,注册完成后,Selector就会去监控这个channel的IO状态(读写,连接)。只有当通道中的数据完全准备就绪,Selector才会将数据分配到服务端的某个线程去处理。
这种非阻塞性的流程就可以更好地去使用CPU的资源。提高CPU的工作效率。这个可以用收快递来说明。如果你一开始就告诉我半小时后过来取快递,而我在这时候已经到目的地了,我有可能就原地不动站着等半个小时。这个期间啥地都去不了,可是你是到了之后,才打电话告诉我过来取,那我就有了更多的自由时间。
2.2 code(阻塞性IO的网络通信)
现在我们来演示一下阻塞性IO的网络通信
2.2.1 client(阻塞性IO)
这个代码大家可以尝试这删除sChannel.shutdownOutput(),此时会发现在启动好server,运行client程序的时候,程序也会阻塞,这是因为这时服务端并无法确定你是否已经发送完成数据了,所以client端也产生了阻塞,双方就一直僵持。
还有一种方法是解阻塞,之后进行阐述。
// 1.获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("你的IP地址",9898));
// 2.创建文件通道
FileChannel inChannel = FileChannel.open(Paths.get("C:/Users/Administrator/Desktop/testPic.jpg"),StandardOpenOption.READ);
// 3.分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 4.发送数据,需要读取文件
while (inChannel.read(byteBuffer) != -1){byteBuffer.flip();// 将buffer的数据写入到通道中sChannel.write(byteBuffer);byteBuffer.clear();
}// 主动告诉服务端,数据已经发送完毕
sChannel.shutdownOutput();while (sChannel.read(byteBuffer) != -1){byteBuffer.flip();System.out.println("接收服务端数据成功···");byteBuffer.clear();}// 5.关闭通道
inChannel.close();
sChannel.close();
2.2.2 server(阻塞性IO)
// 1.获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
// 创建一个输出通道,将读取到的数据写入到输出通道中,保存为testPic2
FileChannel outChannel = FileChannel.open(Paths.get("testPic2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
// 2.绑定端口
ssChannel.bind(new InetSocketAddress(9898));
// 3.等待客户端连接,连接成功时会得到一个通道
SocketChannel sChannel = ssChannel.accept();
// 4.创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 5.接收客户端的数据存储到本地
while (sChannel.read(byteBuffer) != -1){byteBuffer.flip();outChannel.write(byteBuffer);byteBuffer.clear();
}// 发送反馈给客户端// 向缓冲区中写入应答信息byteBuffer.put("服务端接收数据成功".getBytes());byteBuffer.flip();sChannel.write(byteBuffer);// 关闭通道
sChannel.close();
outChannel.close();
byteBuffer.clear();
然后再当我们的客户端运行起来,就会进行copy操作
2.3 Selector完成非阻塞IO
使用NIO完成网络通信需要三个核心对象:
channel:java.nio.channels.Channel接口,SocketChannel,ServerSocketChannel,DatagramChannel
管道相关:Pipe.SinkChannel,Pine.SourceChannel
buffer:负责存储数据
Selector:其中Selector是SelectableChannel的多路复用器,主要是用于监控SelectableChannel的IO状态
2.3.1 client(非阻塞)
// 1.获取通道,默认是阻塞的
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("192.168.80.1",9898));// 1.1 将阻塞的套接字变成非阻塞
sChannel.configureBlocking(false);// 2.创建指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3.发送数据给服务端,直接将数据存储到缓冲区
byteBuffer.put(new Date().toString().getBytes());
// 4.将缓冲区的数据写入到sChannel
byteBuffer.flip();
sChannel.write(byteBuffer);
byteBuffer.clear();// 关闭
sChannel.close();
2.3.2 server(非阻塞)
代码的注释中已经解释了整个过程的做法,这里就不一一展开了。
// 1.获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
// 2.将阻塞的套接字设置为非阻塞的
ssChannel.configureBlocking(false);
// 3.绑定端口号
ssChannel.bind(new InetSocketAddress(9898));// 4.创建选择器对象
Selector selector = Selector.open();// 5.将通道注册到选择器上(这里的第二个参数为selectionKey),下面有解释
// 此时选择器就开始监听这个通道的接收时间,此时接收工作准备就绪,才开始下一步的操作
ssChannel.register(selector,SelectionKey.OP_ACCEPT);// 6.通过轮询的方式获取选择器上准备就绪的事件
// 如果大于0,至少有一个SelectionKey准备就绪
while (selector.select() > 0){// 7.获取当前选择器中所有注册的selectionKey(已经准备就绪的监听事件)Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator();// 迭代获取已经准备就绪的选择键while (selectionKeyIterator.hasNext()){// 8.获取已经准备就绪的事件SelectionKey selectionKey = selectionKeyIterator.next();if (selectionKey.isAcceptable()){// 9.调用accept方法SocketChannel sChannel = ssChannel.accept();// 将sChannel设置为非阻塞// 再次强调,整个过程不能有任何一条阻塞通道sChannel.configureBlocking(false);// 进行数据接收工作,而且把sChannel也注册上选择器让选择器来监听sChannel.register(selector,SelectionKey.OP_READ);}else if (selectionKey.isReadable()){// 如果读状态已经准备就绪,就开始读取数据// 10.获取当前选择器上读状态准备就绪的通道SocketChannel sChannel = (SocketChannel) selectionKey.channel();// 11.读取客户端发送的数据,需要先创建缓冲区ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 12.读取缓冲区的数据while (sChannel.read(byteBuffer) > 0){byteBuffer.flip();// 这里sChannel.read(byteBuffer)就是这个字节数组的长度System.out.println(new String(byteBuffer.array(),0,sChannel.read(byteBuffer)));// 清空缓冲区byteBuffer.clear();}}// 当selectionKey使用完毕需要移除,否则会一直优先selectionKeyIterator.remove();}}
当调用register方法将通道注册到选择器时,选择器对通道的监听事件需要通过第二个参数ops决定
读:SelectionKey.OP_READ(1)
写:SelectionKey.OP_WRITE(4)
连接:SelectionKey.OP_CONNECT(8)
接收:SelectionKey.OP_ACCEPT(16)
若注册时不仅仅只有一个监听事件,则需要用位或操作符连接
int selectionKeySet = SelectionKey.OP_READ|SelectionKey.OP_WRITE
而关于这个selectionKey,它表示着SelectableChannel和Selectr之间的注册关系。它也有一系列对应的方法
2.3.3 客户端的改造
引入Scanner接收输入信息,不过请注意,在测试代码中输入IDEA需要进行一些设置,具体做法是在Help-Edit Custom VM Option中加入一行
-Deditable.java.test.console=true
这样就可以输入了。
// 1.获取通道,默认是阻塞的
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("192.168.80.1",9898));// 1.1 将阻塞的套接字变成非阻塞
sChannel.configureBlocking(false);// 2.创建指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){String str = scanner.next();// 3.发送数据给服务端,直接将数据存储到缓冲区byteBuffer.put((new Date().toString()+str).getBytes());// 4.将缓冲区的数据写入到sChannelbyteBuffer.flip();sChannel.write(byteBuffer);byteBuffer.clear();
}
// 关闭
sChannel.close();
这样就完成了一个问答模式的网络通信。
2.4 Pipe管道
Java NIO中的管道是两个线程之间的单向数据连接,Pipe有一个source管道和一个sink管道,数据会被写到sink,从source中获取
// 1.获取管道
Pipe pipe = Pipe.open();// 2.创建缓冲区对象
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3.获取sink通道
Pipe.SinkChannel sinkChannel = pipe.sink();
byteBuffer.put("通过单向管道传输数据".getBytes());// 4.将数据写入sinkChannel
byteBuffer.flip();
sinkChannel.write(byteBuffer);
// 5.读取缓冲区中的数据
Pipe.SourceChannel sourceChannel = pipe.source();
// 6.读取sourceChannel中的数据放入到缓冲区
byteBuffer.flip();
sourceChannel.read(byteBuffer);
System.out.println(new String(byteBuffer.array(),0,sourceChannel.read(byteBuffer)));sourceChannel.close();
sinkChannel.close();运行结果就是打印了我们的那串字符"通过单向管道传输数据"
相关文章:

NIO基础
一、NIO基础 Java New IO是从Java1.4版本开始引入的一个新的IO api,可以替代以往的标准IO,NIO相比原来的IO有同样的作用和目的,但是使用的方式完全不一样,NIO是面向缓冲区的,基于通道的IO操作,这也让它比传…...

npm修改为国内镜像
npm config get registry 查看下载地址,默认是https://registry.npmjs.org/ 这是国外的地址,npm下载很慢 npm config set registry https://registry.npmmirror.com/ 使用此指令,修改为淘宝镜像,输入指令后检查是否修改成功 …...
php魔术方法和反序列化漏洞
什么是反序列化漏洞? 漏洞形成的根本原因就是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、GetShell 等一系列不可控的后果。反序列化漏洞并不是PHP 特有的,也存在于Java、Pyth…...

依赖项的处理与层的创建与注册
依赖项的处理与层的创建与注册 依赖项的处理与层的创建与注册 新问题什么是 layer?layer 的创建与注册 与函数同时创建和绑定单独上传 layer 再绑定函数(推荐) 真正的运行时依赖 注册包的约定与平台强关联的运行时 1. 云端安装依赖2. 本地构建 Amazon Linux 2 容器环境3. 利用…...

Linux CentOS7 系统中添加用户
在linux centOS7系统中,添加用户是管理员的基本操作。作为学习linux系统的基本操作,对添加用户应该多方面了解。 添加用户的命令useradd,跟上用户名,就可以快速创建一个用户。添加一些选项,可以设置更人性化的用户信息…...

八、任务状态
1、任务状态简介 (1)任务状态可以简单的分为运行和非运行。 (2)非运行状态可以细分为:阻塞状态、暂停状态、就绪状态。 2、阻塞状态(Blocked) (1)举例说明:在日常生活的例子中,母亲在电脑前跟同事沟通时,如果同事一直没回复&a…...
基于python的反爬虫技术的研究设计与实现
摘 要 当下的网络是复杂的,网络上的信息非常的丰富,但也造成了大量的信息堆积,特别是大量的重复信息被反复的推送给用户。这是一个流量的时代,很多社会群体都会聚焦具备流量潜力的信息,从而发生蹭热度等行为来提升自己…...

msvcr120.dll放在哪里?怎么修复msvcr120.dll文件
当您在运行某些应用程序或游戏时遇到“msvcr120.dll缺失”错误时,这可能会影响您的使用体验。msvcr120.dll是Microsoft Visual C Redistributable的一部分,并且它提供了程序运行所需的运行时支持,今天我们来讨论一下msvcr120.dl文件缺失了要怎…...
Ubuntu搭建NFS服务
# 服务器初始化步骤 ## 查看磁盘 fdisk -l ## 格式化磁盘,后面的盘符注意对应关系 mkfs.ext4 /dev/sdc ## 新建文件夹 mkdir /mnt/nfs ## 挂载磁盘到创建的文件夹 echo "/dev/sdc /mnt/nfs ext4 defaults 0 0" >> /etc/fstab ## 重新挂载所有分区…...

PHP教学质量评估系统Dreamweaver开发mysql数据库web结构php编程计算机网页代码
一、源码特点 PHP教学质量评估系统是一套完善的web设计系统,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 代码 https://download.csdn.net/download/qq_41221322/88301983 论文 https://down…...
ElementUI浅尝辄止15:Table 表格
用于展示多条结构类似的数据,可对数据进行排序、筛选、对比或其他自定义操作。 Table组件比较常用,常见于数据查询,报表页面,用来展示表格数据。 1.如何使用? //当el-table元素中注入data对象数组后,在el-t…...
配置LVS_DR模式以及nginx负载均衡
一、配置LVS--DR模式: yum install ipvsadm 配置 LVS 负载均衡服务 ( 1 )手动添加 LVS 转发 1 )用户访问: www.uolookking.com-->vip 192 .168.79.110 ##> 这个是在 DNS 配置 hzitedu 域的 DNS 记录设置 w…...

虚拟数字人直播软件实现带货功能,成为新一代直播风口!
随着短视频带货市场的不断发展,虚拟数字人直播技术逐渐成为热门话题。而在现如今的市场趋势下直播带货则成为了一种火热的营销方式。那么,虚拟数字人直播软件是否可以结合起来,实现无人直播带货的效果呢?让我们来了解一下。 灰豚数…...
01背包问题暴力解法(回溯法)和经典解法
暴力解法(回溯法) import java.util.Arrays; import java.util.Scanner;public class Main {private final static int N 999;public static int SumValue 0;public static int SumWeight 0;public static int OptimalValue 0;public static int O…...

K8S的CKA考试环境和题目
CKA考试这几年来虽然版本在升级,但题目一直没有大的变化,通过K8S考试的方法就是在模拟环境上反复练习,通过练习熟悉考试环境和考试过程中可能遇到的坑。这里姚远老师详细向大家介绍一下考试的环境和题目,需要详细资料的同学请在文…...

docker清理
1. 查看docker 磁盘占用 docker system df 2. 参考: Docker磁盘占用与清理问题_docker system prune_蓝鲸123的博客-CSDN博客...
队列和栈两种数据结构的区别和Python实现
队列和栈是两种数据结构,其内部都是按照固定顺序来存放变量的,二者的区别在于对数据的存取顺序 栈是最后存入的数据最先取出,即后进先出 队列是先存入的数据最先取出,即先进先出 Python实现栈 使用append()方法存入数据,使用pop()方法读取数据 # 定义一个空列表(当做栈使…...

java 企业工程管理系统软件源码+Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis
鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管…...

使用Smartctl脚本输入当前所有磁盘的状态
一、安装Smartctl yum install smartmontools 二、写一个脚本输出当前所有磁盘的状态并且按名称分别写入到文件中 #!/bin/bashfor dev in $(lsblk -l | grep disk | awk {print $1}) doecho "检测磁盘 $dev"smartctl -a /dev/$dev > $dev.smartctl done 以下是这…...

数学建模之插值法
目录 1 插值法概述2 插值法原理3 拉格朗日插值4 牛顿插值5 三次Hermite插值(重点)6 三次样条插值(重点)7 各种插值法总结8 n 维数据的插值9 插值法拓展10 课后作业 1 插值法概述 数模比赛中,常常需要根据已知的函数点进…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...