网站开发基本要求/阿森纳英超积分
Java Web 实战 02 - 多线程基础篇 - 1
- 一 . 认识线程
- 1.1 概念
- 1.1.1 什么是线程?
- 1.1.2 为什么要有多个线程?
- 1.1.3 进程和线程的区别(面试题)
- 1.2 第一个多线程程序
- 1.3 创建线程
- 1.3.1 继承Thread类
- 1.3.2 实现Runnable接口
- 1.3.3 继承 Thread 类 , 使用匿名内部类
- 1.3.4 实现 Runnable , 使用匿名内部类
- 1.3.5 lambda 表达式来定义任务(推荐)
- 1.4 多线程的好处
- 1.5 多线程的使用场景
- 1.6 小结
大家好 , 这篇文章给大家带来的是多线程相关的基础知识 , 我们先介绍一下什么是线程、创建线程的方法、多线程的好处以及使用场景等。
由于 C 站的编辑器不太好用 , 导致许多排版没能生效 , 大家可移步至这里观看
https://www.yuque.com/jialebihaitao/study/qzym2pw332lm6k7q?singleDoc# 《2. 多线程 (基础)》
感谢大家的支持~
一 . 认识线程
1.1 概念
1.1.1 什么是线程?
线程和进程之间 , 确实有一定的联系 .
线程(Thread) : 更加轻量的进程 , 也是一种实现并发编程的方案 , 创建线程和销毁线程的时候 , 比创建进程销毁进程更加轻量
个人理解 : 线程是比进程还小的单位 , 一个进程里面有多个线程 , 每个线程分别完成自己的任务 , 他们可以同时进行
举个栗子 :
一家三口来饭店吃饭 , 可是这个时候只有老板在 , 端茶做菜忙活不过来 , 所以就把老板娘叫过来了 . 这时候 , 就有两个线程 “老板” “老板娘”
此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别排队执行。其中老板娘是老板叫来的,所以老板我们一般被称为主线程
1.1.2 为什么要有多个线程?
我们先来思考个问题,为什么要有多个进程?
CPU 单个核心已经开发到极致了 , 要想提升算力 , 就需要用多个核心 . 就需要"并发编程"
引入并发编程最大的目的就是为了能够充分的利用好 CPU 的多核资源。
使用多进程这种编程模式是完全可以做到并发编程的 , 并且也能够使 CPU 多核被充分利用
但是在有些场景下会存在问题。
如果需要频繁的 创建 / 销毁 进程,多进程这个时候就会比较低效。
例如你写了一个服务器程序,服务器要同一时刻给很多客户提供服务,那么这个时候就要用到并发编程了
典型的做法就是给每个客户端分配一个进程 , 提供一对一的服务。客户端访问就创建,客户端离开了就销毁。
但是创建 / 销毁 进程本身就是一个比较低效的操作。
创建PCB
PCB 也叫 进程控制块。它的作用是操作系统表示进程的属性的结构体 , 这个结构体里就包含了一些表示进程的核心信息。
分配系统资源(尤其是内存资源)
分配资源就比较浪费时间了
这个是要在系统内核资源管理模块 , 进行一系列遍历操作的把PCB加到内核的双向链表中
为了提高这个场景下的效率,我们就引入了线程这个概念 , 线程也叫做"轻量级进程"。
一个线程其实是包含在进程中的。(一个进程里面可以有很多个线程)
每个线程其实也有自己的 PCB (所以一个进程里面有可能对应多个 PCB )
同一个进程里的多个线程之间共用同一份系统资源。
这就意味着新创建的线程不必重新分配系统资源,只需要复用之前的即可。
因此创建线程只需要 :
- 创建PCB
- 把PCB加到内核的链表中
这就是线程相对于进程做出的重大的改进,也就是线程更轻量的原因。
举个栗子吧
江南皮革厂老总生意非常好 , 他想扩充他的生意。现在有两种方案
方案一 : 再租个厂子
方案二 : 在原来的厂子基础上进行扩建。
线程是包含在进程内部的"逻辑执行流"。(线程可以执行一段单独的代码,多个线程之间是并发举行的。)
操作系统进行调度的时候,其实也是以"线程为单位"进行调度的。
创建线程的开销比创建进程要小,销毁线程的开销也比销毁进程要小
那么如果把进程比作一座工厂 , 线程就是工厂内部的流水线
再举个栗子 :
1.1.3 进程和线程的区别(面试题)
- 进程是包含线程的 , 线程是在进程内部的
每个进程至少有一个线程 , 叫做主线程
-
每个进程有独立的虚拟地址空间 , 也有自己独立的文件描述符表 . 同一个进程的多个线程之间 , 共用这一份虚拟地址空间和文件描述符表
-
进程是操作系统中"资源分配"的基本单位 , 线程是操作系统中"调度执行"的基本单位
-
多个进程同时执行的时候 , 如果一个进程挂了 , 一般不会影响到别的进程但是同一个进程中的多个线程之间 , 如果一个线程挂了 , 就很有可能把整个进程带走 , 同一个进程中的其他线程也就没了
1.2 第一个多线程程序
即使是一个最简单的"Hello World"程序 , 其实在运行的时候 , 也涉及到线程了 .
public class Main {public static void main(String[] args) {System.out.println("Hello World");}
}
虽然在上述代码中 , 我们并没有手动创建其他线程
但是 Java 程序在运行的时候 , 内部也会创建出多个线程 .
一个进程里面至少会有一个线程 , 运行这个程序 , 操作系统就会创建出一个 Java 进程 , 在这个 Java 进程里面就会有一个线程调用 main 方法
谈到多进程 , 经常会谈到"父进程" “子进程”
进程A里面创建了进程B
A是B的父进程 , B是A的子进程
但是在多线程里面 , 没有"父线程" "子线程"这种说法 , 即使它们之间也存在创建与被创建的关系 , 但是仍然认为线程之间地位是相等的
1.3 创建线程
1.3.1 继承Thread类
- 继承 Thread 类来创建一个线程类 , 然后重写里面的 run 方法
class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello Mythread");}
}
- 创建 MyThread 类的实例
Thread t = new MyThread();
- 调用 start 方法启动线程
t.start();
整体的代码 :
class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello Mythread");}
}
public class Demo1 {public static void main(String[] args) {// 创建一个线程// 在Java中,创建一个线程,离不开一个重要的类:Thread// 创建方式:写一个子类,继承Thread,重写里面的run方法Thread t = new MyThread();//向上转型:父类类型 对象名 = new 子类类型();t.start();System.out.println("Hello main");}
}
在 start 之前,线程只是准备好了,并没有真正被创建出来,只有执行了 start 方法之后,才在操作系统中真正创建了线程。
在操作系统中创建线程:
- 创建 PCB
- 把 PCB 加到链表中
那么我们来看一下运行结果
在这个代码中 , 虽然我们是先启动线程 , 再打印 “Hello main”
但是实际运行结果 , 是先打印 “Hello main” , 再打印 “Hello MyThread”
那这是怎么回事呢 ?
- 每个线程是独立的执行流
main 对应的线程是一个执行流
MyThread 对应的线程又是一个执行流
main 线程和 MyThread 各跑各的 , 互不影响 .
这两个执行流是 并发(并行+并发) 的执行关系
- 此时两个线程执行的先后顺序 , 取决于操作系统调度器的具体实现
这里面的调度器里面的调度规则 , 可以简单地视为"随机调度"
因此我们看到的虽然是先打印 “Hello main” , 后打印 “Hello MyThread” , 但是不是一直都是这样的 . 当前看到的先打印 main , 大概率是因为受到创建线程自身的开销影响的
哪怕我们运行1000次 , main在前 , 我们也不能说第1001次的时候 , main还是在前面的 .
所以我们需要注意 : **编写多线程的代码的时候 , 默认情况下 , 代码是无序执行的 , 是操作系统"随机调度的" , 所以我们不要想当然的认为多线程的执行顺序是从上到下的 . **
但是我们还是可以影响到线程执行的先后顺序的 , 但是调度器自身的"随机调度"的行为修改不了
调度器依然是"随机调度" , 咱们最多能做到的就是让某个线程先等待一会 , 等待另一个线程执行完了我们再去执行
那么我们再来看一下进程结束的信息
我们觉得之前运行的太快了 , 我们可以让他不结束 , 这样就可以观察一下里面都有什么线程了
class MyThread extends Thread {@Overridepublic void run() {while(true) {System.out.println("Hello Mythread");}}
}
public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();while(true) {System.out.println("Hello main");}}
}
我们可以看到 , 程序进入死循环正在疯狂执行 , 那么我们可以使用 Java 官方提供给我们的工具来查看有哪些线程
我们找到 JDK 的安装目录 , 里面的 bin 目录有个 jconsole 工具
一进来就看到了我们的进程 , 点击连接 , 我们就能看到线程具体信息了 .
要注意的情况是 :
想要查看具体的线程信息 , 需要保证程序是一直在运行的状态 , 如果你把程序终止运行了 , 那么你就观察不到任何信息了
还有可能是正在运行但是还是什么也看不到 , 那么尝试一下用管理员方式打开再去试一下 , 应该就没问题了 .
那么刚才的程序执行的太快了 , 不方便我们观察执行结果 .
那么我们可以使用 sleep
函数 , 来让线程适当休息一下
使用
**Thread.sleep()**
的方式进行休眠
sleep 是 Thread 的静态方法 , 类名直接调用即可
sleep 函数的参数是时间 , 单位是 ms , 意思是想让线程休息多长时间
时间单位的换算 :
1 s = 1000 ms
1 ms = 1000 us
1 us = 1000 ns
1 ns = 1000 ps
进行一次网络通信 , 花的时间大概就是 us-ms 级的
进行一次读写硬盘 , 花的时间大概就是 ns-us 级的
进行一次读写内存 , 花的时间大概就是 ps - ns 级的
我们发现 , 当我们写上 sleep 函数的时候 , 报错了 .
所以我们要处理一下 , 点击报错位置 ,alt + 回车
就自动把异常捕获了 , 但是 run 函数这里面只能使用 try catch语句 , 不能使用其他的捕获异常的操作 .
因为 run 函数是重写的方法 , 原函数就没有提供其他的捕获异常的方法
我们的 main 方法里面要进行异常捕获 , 就有两种方法了 , 一个是 try catch , 一个是 throws 抛出异常 , throws 是交给上一层(在这里面就是JVM)来处理 , 推荐使用 try catch 进行捕获
class MyThread extends Thread {@Overridepublic void run() {while(true) {System.out.println("Hello Mythread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();while(true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
查看一下运行结果 :
那么这里面就会有一个常见面试题 :
谈一谈 Thread 的 run 和 start 的区别
使用 start , 可以看到两个线程并发执行 , 两组打印交替出现
使用 run , 只打印 Hello MyThrad , 没有打印 Hello main
直接调用 run , 并没有创建出新的线程 , 只是在之前的线程中(在这里面也就是main线程) , 执行了 run 里面的内容 , 所以只打印了 Hello MyThread
使用 start , 则是创建新的线程 , 新的线程里面调用 run , 新线程和旧线程之间是并发执行的关系
1.3.2 实现Runnable接口
class MyRunnable implements Runnable {@Overridepublic void run() {while(true) {System.out.println("Hello Mythread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Demo2 {public static void main(String[] args) {Runnable runnable = new MyRunnable();Thread t = new Thread(runnable);t.start();while(true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
//线程本身:Runnable runnable = new MyRunnable();
//任务内容:Thread t = new Thread(runnable);
//这两句代码,把任务内容和线程本身,给分离开了,这样耦合度就降低了。
//这样的好处就是让任务的内容和线程关系不大,假设这个任务不想通过多线程执行了,换成别的方式执行,这时候代码的改动也不会特别大
那么有个问题,为什么刚才使用 Thread、Runnable、Interruption 等都不需要 import?
因为这几个类都在 java.lang 里面,默认自动导入的
还有一种情况,是不需要导包的 ,那就是这几个类在同一个包里面,就不需要导包。
1.3.3 继承 Thread 类 , 使用匿名内部类
这个方法仍然是继承 Thread 类,但是不再显式继承,而是使用"匿名内部类"
我们之前在数据结构里面学过,使用优先级队列 PriorityQueue 就可以使用 Comparable 或者 Comparator 来指定比较规则。
使用这两个接口的时候,就可以使用匿名内部类的写法
public class Demo2 {public static void main(String[] args) {Thread t = new Thread() {@Overridepublic void run() {while(true) {System.out.println("Hello MyThread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();while(true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
1.3.4 实现 Runnable , 使用匿名内部类
public class Demo3 {public static void main(String[] args) {Runnable runnable = new Runnable() {@Overridepublic void run() {while(true) {System.out.println("Hello MyThread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};Thread t = new Thread(runnable);t.start();while(true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
我们还可以这样写
public class Demo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while(true) {System.out.println("Hello MyThread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});t.start();while(true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
1.3.5 lambda 表达式来定义任务(推荐)
public class Demo5 {public static void main(String[] args) {Thread t = new Thread(() -> {while(true) {System.out.println("Hello MyThread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();while(true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
这种写法非常简洁,采用了 lambda 表达式 , lambda 表达式的写法是不需要重写 run 方法的
lambda表达式其实也是一种匿名函数(只使用一次,还没有名字)
像lambda表达式这种能简化代码的,叫做"语法糖"
除了上面这5种创建线程的方式,还有好几种,不介绍了,其实至少有7种方式来可以创建线程
对于创建线程的方式到底有哪些,这也是个经典面试题了
1.4 多线程的好处
使用多线程,能够充分的利用CPU多核资源
比如这个操作:
public class Demo7 {private static final long num = 20_0000_0000;//20亿public static void serial() {long begin_time = System.currentTimeMillis();long n1 = 0;for (long i = 0; i < num; i++) {n1++;}long n2 = 0;for (long i = 0; i < num; i++) {n2++;}long end_time = System.currentTimeMillis();System.out.println("单线程消耗的时间:" + (end_time - begin_time) + "ms");}public static void main(String[] args) {serial();}
}
我们明显可以看到:单线程执行的效率是不太高的,基本运行了大约2s,直接就给人一个等待的感觉了。
那么我们来看看多线程的速度如何?
public class Demo8 {private static final long num = 20_0000_0000;public static void concurrency() {long begin_time = System.currentTimeMillis();Thread t1 = new Thread(() -> {long a = 0;//注意:a b不能在外面定义,访问不到for (long i = 0; i < num; i++) {a++;}});Thread t2 = new Thread(() -> {long b = 0;for (long i = 0; i < num; i++) {b++;}});t1.start();t2.start();long end_time = System.currentTimeMillis();System.out.println("多线程消耗的时间:" + (end_time - begin_time) + "ms");}public static void main(String[] args) {concurrency();}
}
多线程确实嗷嗷快。
但是有一个问题:
这个代码涉及到三个进程 t1 t2 main ,他们都是并发执行的,谁先执行完谁后执行完是不确定的 , 很有可能 t1 t2 线程还没执行完 , main 线程就结束战斗了 .
就比如 1000m 长跑,main 是裁判,t1 t2 是两名运动员。t1 t2 两个兄弟在 main 的一声哨响中出发了,然后main 就直接停表了 , 那么实际上测出的 t1 t2 两名运动员的成绩误差是很大的,所以我们需要注意一下,我们可以采取让 main 等到 t1 t2 到达终点再停止计时 . 这里我们需要用到 join 这个关键字
join关键字 是等待线程结束
在这里的意思就是等待 t1 t2 结束 , main 线程再结束
在主线程当中调用 **t1.join()**
意思就是让 main 线程等待t1执行完
所以代码应该改成这样才合理
public class Demo8 {private static final long num = 20_0000_0000;public static void concurrency() {long begin_time = System.currentTimeMillis();Thread t1 = new Thread(() -> {long a = 0;for (long i = 0; i < num; i++) {a++;}});Thread t2 = new Thread(() -> {long b = 0;for (long i = 0; i < num; i++) {b++;}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}long end_time = System.currentTimeMillis();System.out.println("多线程消耗的时间:" + (end_time - begin_time) + "ms");}public static void main(String[] args) {concurrency();}
}
这时候的运行时间才更精确一些
我们可以把单线程的执行方式和多线程的执行方式放在一起比较一下
public class Demo9 {private static final long num = 20_0000_0000;//20亿public static void serial() {long begin_time1 = System.currentTimeMillis();long n1 = 0;for (long i = 0; i < num; i++) {n1++;}long n2 = 0;for (long i = 0; i < num; i++) {n2++;}long end_time1 = System.currentTimeMillis();System.out.println("单线程消耗的时间:" + (end_time1 - begin_time1) + "ms");}public static void concurrency() {long begin_time = System.currentTimeMillis();Thread t1 = new Thread(() -> {long a = 0;for (long i = 0; i < num; i++) {a++;}});Thread t2 = new Thread(() -> {long b = 0;for (long i = 0; i < num; i++) {b++;}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}long end_time = System.currentTimeMillis();System.out.println("多线程消耗的时间:" + (end_time - begin_time) + "ms");}public static void main(String[] args) {serial();concurrency();}
}
看一下运行结果 , 多线程要比单进程快一倍左右
那么我们之前没加 join 的时候 , 多线程不是很快嘛 , 这怎么加上 join 之后咋还慢了?
加上 join 之后才是裁判等到选手到终点,才停止计时。
如果没有 join 的限制,main、t1、t2都是同时向下走的,走的过程中,调度顺序是不确定的。
最极端的情况就是一直在执行 main 线程 , t1 t2 两个线程都没有被执行 , 这样的结果肯定是不对的
有可能先执行 main ,再执行 t1 ,再执行 t2
有可能先执行 t1,再执行 t2,再去执行 main,再去执行 t1…
是有很多种可能性的。
那么我们继续来看 , 单线程消耗时间的时间是1095ms,多线程消耗的时间是581ms,那么为什么不是单线程执行时间的一半呢(即547.5s)呢?
- 创建线程,也是有开销的
- 两个线程在CPU上不一定就是纯并发执行,有可能一部分时间并行执行,一部分时间并发执行
- 线程的调度也是有开销的。
1.5 多线程的使用场景
- 在CPU密集型区域
代码中的大部分工作,都是在使用CPU进行计算,使用多线程,就可以充分利用CPU多核资源,可以提高效率
- 在IO密集型场景
I 指的是 Input,O 指的是 Output
读写磁盘、读写网卡等等这些都是 IO 操作,需要花费很长的时间等待,但是像这种 IO 操作,基本都是不消耗 CPU 就可以快速完成的工作,那么这时候 CPU 就在摸鱼,就可以给 CPU 找点活干
比如在食堂打饭,要排队很久,有的同学就拿出手机背背单词,这个时候就算是等待 IO 结束,我们给 CPU指定点活
1.6 小结
多线程的创建顺序是由 start 的顺序决定的 , 但是执行顺序是不确定的 , 这取决于系统的调度器怎么处理
意思就是我们先创建线程,不一定就是先去执行。
举个栗子:
t1.start();
t2.start();
t3.start();
这样的情况就不知道谁先被执行了 , 具体线程里的任务啥时候执行 , 要看调度器
比如:我跟 A B C 依次确定了关系 , 但是我不一定第一天就去跟 A 搞事情 , 跟 B/C 谁发生关系这都是不确定的 , 视心情而定
但是我们这样呢
t1.start();
t1.sleep(1000);
t2.start();
t3.start();
这样的意思就是我跟 A 确定关系一年了,我再去跟 B C 接触
那么大概率就是先执行 A (因为都接触一年了) , 也不排除先执行 B/C (可能网恋,见不了面 , 就先跟本地的 B/C 交往)
到此 , 本篇文章就结束了 , 敬请期待后续!
相关文章:

Java Web 实战 02 - 多线程基础篇(1)
Java Web 实战 02 - 多线程基础篇 - 1一 . 认识线程1.1 概念1.1.1 什么是线程?1.1.2 为什么要有多个线程?1.1.3 进程和线程的区别(面试题)1.2 第一个多线程程序1.3 创建线程1.3.1 继承Thread类1.3.2 实现Runnable接口1.3.3 继承 Thread 类 , 使用匿名内部类1.3.4 实现 Runnab…...

C/C++开发,无可避免的多线程(篇三).协程及其支持库
一、c20的协程概念 在c20标准后,在一些函数中看到co_await、co_yield、co_return这些关键词,这是c20为协程实现设计的运算符。 协程是能暂停执行以在之后恢复的函数。原来我们调用一个功能函数时,只要调用了以后,就要完整执行完该…...

高级信息系统项目管理(高项 软考)原创论文项目背景合集
以下为原创的高项论文项目背景合集5篇,建议自己以此为基础,再多多打磨完善一下,避免雷同,同时使项目背景更加真实可信。 一、某市智慧工地系统建设项目 某市住建局智慧工地系统建设项目是在该市住建局促进建筑行业转型升级和科技创新,强化工程质量安全,推动建筑业高质量…...

锁屏面试题百日百刷-Hive篇(十一)
锁屏面试题百日百刷,每个工作日坚持更新面试题。锁屏面试题app、小程序现已上线,官网地址:https://www.demosoftware.cn。已收录了每日更新的面试题的所有内容,还包含特色的解锁屏幕复习面试题、每日编程题目邮件推送等功能。让你…...

一看就懂,等保2.0工作流程这么做
等保2.0相关国家标准于2019年12月1日开始实施,标志着我国网络安全等级保护工作进入一个崭新的阶段,对于加强我国网络安全保障工作,提升网络安全保护能力具有十分重要的意义。很多行业主管单位要求行业客户开展等级保护工作,合理地…...

Kerberos 域委派攻击之非约束性委派
CSDN文章自动迁移自博客在Windows 2000 Server 首次发布 Active Directory 时,Microsoft 必须提供一种简单的机制来支持用户通过 Kerberos 向 Web Server 进行身份验证并需要代表该用户更新后端数据库服务器上的记录的方案。这通常称为“Kerberos 双跳问题”&#x…...

【容器运行时】一文理解 OCI、runc、containerd、docker、shim进程、cri、kubelet 之间的关系
参考 docker,containerd,runc,docker-shim 之间的关系Containerd shim 进程 PPID 之谜内核大神教你从 Linux 进程的角度看 DockerRunC 简介OCI和runCContainerd 简介从 docker 到 runCDockershim究竟是什么技术干货|Docker和 Con…...

spark兼容性验证
前言 Apache Spark是专门为大规模数据处理而设计的快速通用的计算引擎,Spark拥有Hadoop MapReduce所具有的优点,但不同于Mapreduce的是Job中间输出结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好的适用于数据挖掘与…...

docker逃逸复现--pid=host模式下的逃逸
漏洞原理当docker以--pidhost模式启动时,你可以通过在容器进程中注入一些shellcode进行逃逸。相当于给了docker Linux中的CAP_SYS_PTRACE权限--pidhost:意味着宿主机与容器公享一套pid,如此做容器就可以访问并跟踪宿主机的进程Linux中的CAP_S…...

【环境配置】Windows系统下搭建Pytorch框架
【环境配置】Windows系统下搭建Pytorch框架 在Windows Serve 2019系统下搭建Pytorch框架 目录 【环境配置】Windows系统下搭建Pytorch框架1.用驱动总裁安装显卡驱动2.在cmd运行nvidia-smi3.安装cuda4.安装cudnn5.安装pytorch的命令1.首次安装2.操作失误需要重新安装6.安装torc…...

Dockerfile简单使用入门
什么是 Dockerfile? Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。 docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile。 例如࿱…...

什么是CCC认证3C强制认证机构
什么是CCC认证3C强制认证机构? 3C认证的全称为“强迫性产物认证轨制”,它是中国政府为掩护消费者人身平安和国度平安、增强产物品质治理、按照法律法规履行的一种产物及格评定轨制。所谓3C认证,便是中国强迫性产物认证轨制,英文名…...

C语言-基础了解-18-C共用体
C共用体 一、共用体 共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式 二、定义共同体 为了定义共用体&…...

Vue基础18之github案例、vue-resource
Vue基础18github案例静态页面第三方样式引入(以bootstrap举例)App.vueSearch.vueList.vue列表展示接口地址使用全局事件总线进行兄弟间组件通信Search.vueList.vue完善案例List.vueSearch.vue补充知识点:{...this.info,...this.dataObj}效果呈…...

UE4 c++ Mediaplayer取消自动播放,运行时首帧为黑屏的问题
0,前言 工作需要使用C制作一个ue4的视频插件,其中一个功能是能够选择 运行时是否自动播放 视频的功能。 在实现时遇见了一个问题,取消自动播放之后,运行时首帧是没有取到的,在场景里面看是黑色的。就这个问题我想到了使…...

C语言-基础了解-17-C结构体
C结构体一、c结构体C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。结构体中的数据成员可以是基本数据类型(如 int、float、char 等),也可…...

Python爬虫实践:优志愿 院校列表
https://www.youzy.cn/tzy/search/colleges/collegeList获取目标网址等信息打开开发人员工具(F12),拿到调用接口的地址,以及接口请求参数等信息,如下curl https://uwf7de983aad7a717eb.youzy.cn/youzy.dms.basiclib.ap…...

Java框架学习 | MySQL和Maven笔记
1.MySQL提问式思考 为什么要有数据库?MySQL的优劣势?Java的优劣势? JavaMySQL开源具有大量的社区成员和丰富的资源免费/具有大量的社区成员和丰富的资源可扩展性多态、继承和接口等分区、复制和集群等方式扩展数据库的容量和性能安全性有许…...

C++入门教程||C++ 变量作用域||C++ 常量
C 变量作用域 作用域是程序的一个区域,一般来说有三个地方可以声明变量: 在函数或一个代码块内部声明的变量,称为局部变量。在函数参数的定义中声明的变量,称为形式参数。在所有函数外部声明的变量,称为全局变量。 我…...

想找工作,这一篇15w字数+的文章帮你解决
文章目录前言一 专业技能1. 熟悉GoLang语言1.1 Slice1.2 Map1.3 Channel1.4 Goroutine1.5 GMP调度1.6 垃圾回收机制1.7 其他知识点2. 掌握Web框架Gin和微服务框架Micro2.1 Gin框架2.2 Micro框架2.3 Viper2.4 Swagger2.5 Zap2.6 JWT3. 熟悉使用 MySQL 数据库3.1 索引3.2 事务3.3…...

Mac brew搭建php整套开发环境
Homebrew完整版,安装时间较长/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"精简版/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" speednginxBrew sear…...

111 e
全部 答对 答错 单选题 4.一个项目已经执行了两个多月,出乎意料的是,项目经理收到一封来自高级管理层的电子邮件,指出项目发起人正在请求变更项目开工会议的日期,项目经理未能执行哪项活动? A为项目管理计划制定基准…...

Cookie和Session
1. Cookie饼干 1.1 什么是Cookie? Cookie翻译过来就是饼干的意思Cookie是服务器通知客户端保存键值对的一种技术客户端有了Cookie后,每次请求都发送给服务器每个Cookie的大小不能超过4kb 1.2 如何创建Cookie BaseServlet 程序 package com.gdhd;impo…...

git上传下载
拉取: 先在电脑中创建一个文件夹用来存放要从码云上拉下来的项目并且用Git打开输入 git remote add origin + (想要下拉的项目的地址http/ssh)第一次拉取代码,输入码云的用户名(自己设置的个人地址名)和码云的账号密码 git pull origin master 拉取完成OK 上传: 进行 G…...

如何使用码匠连接 Oracle
目录 在码匠中集成 Oracle 在码匠中使用 Oracle 关于码匠 Oracle 是一种关系型数据库,可用于存储和管理大量结构化数据。Oracle 数据源支持多种操作系统,包括 Windows、Linux 和 Unix 等,同时也提供了各种工具和服务,例如 Orac…...

【Git】git常用命令集合
目录最常用的git命令git拉取代码git本地如何合并分支上传文件识别大小写开发分支(dev)上的代码达到上线的标准后,要合并到master分支当master代码改动了,需要更新开发分支(dev)上的代码git本地版本回退与远…...

基于 WebSocket、Spring Boot 教你实现“QQ聊天功能”的底层简易demo
目录 前言 一、分析 1.1、qq聊天功能分析 1.2、WebSocket介绍 1.2.1、什么是消息推送呢? 1.2.2、原理解析 1.2.3、报文格式 二、简易demo 2.1、后端实现 2.1.1、引入依赖 2.1.2、继承TextWebSocketHandler 2.1.3、实现 WebSocketConfigurer 接口 2.2、…...

13. 郭老师爱合并果子
1 题目描述 郭老师爱合并果子成绩20开启时间2021年10月8日 星期五 18:00折扣0.8折扣时间2021年10月26日 星期二 00:00允许迟交否关闭时间2021年12月1日 星期三 00:00 郭老师家有个果园,每年到了秋收的时候都会收获很多不同种类的果子。他决定把所有的果子合成一堆&…...

Method breakpoints may dramatically slow down debugging 解决方案
项目无法启动了 简单介绍一下事情的过程:昨天在进行代码调试的时候,代码部分处理完成之后,启动debug模式的热部署准备测试一下逻辑,结果左下角提示我热部署失败,需要重新启动Tomcat才能再次调试,所以只得重…...

ABAP ALV和OOALV设置单元格颜色,编辑
首先给大家分享一篇博客: REUSE_ALV_GRID_DISPLAY_LVC-可编辑单元格 文章目录单元格编辑单元格/行-颜色效果展示**需求:**我是想实现某个单元格可根据数据来判断是否是可以进行编辑的或要添加一个什么样的颜色. 我们需要用到下面的三个结构 ALV 控制: 单元格的类型表:LVC_T_ST…...