dedecms 网站安全设置/西安网站制作推广
Java Web 实战 03 - 多线程基础篇 2
- 二 . Thread类常见方法
- 2.1 Thread 的常见构造方法
- 2.2 Thread 的几个常见属性
- getId()
- getName()
- getState()
- getPriority()
- isDaemon()
- 案例 : 实现 getId()、getName()、 getState()、getPriority()、isDaemon()、isAlive()
- 2.3 启动一个线程-start()
- 2.4 中断线程(线程执行结束)
- 2.4.1 手动创建的标志位
- 2.4.2 Thread自带的标志位
- 2.5 线程等待 join
- 2.5.1 join 是怎么回事?
- 2.5.2 栗子1 : 让 main 等待 t2 , t2 去等待 t1
- 2.5.3 栗子2 : 控制 main 先运行 t1 , t1 执行完再运行 t2
- 2.5.4 带参数版本的 join
- 2.6 获取到线程引用 : Thread.currentThread()
- 2.7 线程的休眠 : sleep
- 三 . 线程的状态
- 3.1 通过代码来看线程的状态
- 3.2 线程状态和状态转移的意义
大家好 , 这篇文章给大家带来的是多线程相关的基础知识 , 我们会介绍一下Thread 类常见的方法都有什么 , 以及启动线程、中断线程、线程等待、获取线程引用、现成的休眠等问题 , 然后再给大家介绍一下线程的状态都有什么这几个问题。
上一篇文章的链接在这里 , 大家移步观看
http://t.csdn.cn/JVErX
由于 C 站的编辑器不太好用 , 导致许多排版没能生效 , 大家可移步至这里观看https://www.yuque.com/jialebihaitao/study/qzym2pw332lm6k7q?singleDoc# 《2. 多线程 (基础)》
感谢大家的支持~
二 . Thread类常见方法
2.1 Thread 的常见构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 (已经用过了) |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, String name) | 使用 Runnable 对象创建线程对象,并命名 |
其中第一个和第二个我们已经使用过
- Thread() : 自己创建线程的子类 , 再去 new 他的实例
Thread t = new MyThread();
- Thread(Runnable target) : 使用 Runnable 创建一个任务 , 然后将 Runnable 的任务放到线程中 , 这样也可以创建一个线程
Runnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
- Thread(String name) : 这个操作是给线程起名字,我们之前看到的 Thread-0 就是 Java 帮我们命名的,线程在操作系统里面是没有名字的,Java 为了帮助程序员便于理解,就在 JVM 里面给对应的 Thread 对象起了个名,这个线程是和内核中的线程一一对应的 , 这个名字对于程序的执行 , 没有任何影响 , 对于程序员来说调试挺有用的
Thread t3 = new Thread("这是我的名字");
- Thread(Runnable target, String name) : 既实现了命名 , 又实现了使用 Runnable 对象创建线程对象
public class Demo7 {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();}}}
}
我们使用 jconsole 工具查看当前线程 , 就发现有我们自己重新命名的线程了
2.2 Thread 的几个常见属性
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isinterrupted() |
getId()
获取到的是 线程 在 JVM 里面的身份标识
线程里面的身份标识是有好几个的
- 内核的 PCB 上有标识
- 用户态线程库里面也有标识 (pthread , 操作系统提供的线程库)
- JVM里面也有标识 (JVM 的 Thread 类底层也是调用操作系统的 pThread 库)
这三个标识各不相同,但是目的都是作为身份的区分,我们可以理解为身份标识的各种小名
getName()
获取在Thread的构造方法里面,传入的姓名 (刚才在 Thread 构造方法中传入的名字)
getState()
获取到 PCB 里面的状态 , 这个状态表示线程当前所处的一个情况 . 此处得到的状态是 JVM 里面设立的状态体系 , 这个状态比操作系统内置的状态要更丰富
getPriority()
获取到线程的优先级 , 优先级高的线程理论上来说更容易被调度到
isDaemon()
判断是不是后台线程
Daemon的意思是 “守护线程” 的意思 , 我们可以理解为 “后台线程”
线程分为"前台线程"与"后台线程" , 我们可以通过类比手机的正在运行应用 (前台应用) 与后台应用来理解前台线程与后台线程
一个线程 , 创建出来默认就是前台线程 , 前台线程会阻止进程结束 , 进程会保证所有的前台线程都执行完毕 , 才会退出 . main 线程就是一个前台线程
如果我们屏蔽了 main 线程 , 正常情况下线程就会退出
但是运行并非如此
这是因为我们上面新建的线程默认就是前台线程 , 我们需要将自己创建的线程修改成后台线程
public class Demo7 {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.setDaemon(true);// 设置成后台线程t.start();// while(true) {// System.out.println("Hello main");// try {// Thread.sleep(1000);// } catch (InterruptedException e) {// e.printStackTrace();// }// }}
}
这样我们的线程就会嗖的一下执行完
有个小问题 :
❗ 我们发现 , 怎么运行了多次 , 怎么有的时候打印有的时候就不打印呢?
✅ 举个栗子 : 当我们刚打完一局游戏 , 想要打开 B 站去学习 , 这时候点开了 B 站 , B 站这个时候就是"前台应用" , 必须是在执行的 , 不能被销毁
前台线程 : 前台线程会阻止进程结束 , 一个线程创建出来默认就是前台线程 , 进程会保证所有前台进程执行完毕才结束程序
后台的游戏就被隐藏到任务栏里面了 , 其实还运不运行已经无所谓了 , 因为我们已经玩完了
后台线程 : 后台线程不会阻止进程结束 , 比如 : main线程执行结束 , 整个线程就结束了
这是因为我们把 t 线程设置成了后台线程 , 当 main线程执行完毕整个程序就执行完了 , 我们这里并未对 main 线程做任何操作 , 所以有可能是在 main 线程结束之前运行了一下 t 线程 , 也有可能是直接就执行结束了 , t 线程还没来得及执行
案例 : 实现 getId()、getName()、 getState()、getPriority()、isDaemon()、isAlive()
接下来 , 我们写一个方法 , 来实现 getId()、getName()、 getState()、getPriority()、isDaemon()、isAlive()
public class Demo8 {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();}}},"俺的线程");System.out.println(t.getId());// 获取线程IDSystem.out.println(t.getName());// 获取线程名称System.out.println(t.getState());// 获取线程状态System.out.println(t.getPriority());// 获取线程优先级System.out.println(t.isDaemon());// 获取是否是后台线程System.out.println(t.isAlive());// 获取线程是否存活}
}
2.3 启动一个线程-start()
我们之前已经使用过这个方法了
给大家强调一点 :
(安排任务)创建 Thread 实例 , 并没有真正的在操作系统内核中创建出线程 . 这里的任务是通过 Thread 的 run 或者 Runnable 或者 lambda 来体现具体的任务内容
run 方法只是描述任务 , 这一点要与 start 方法区分开
(发令枪响)调用 start 方法 , 才是真正在系统里创建出线程 , 才真正开始执行任务
2.4 中断线程(线程执行结束)
线程的执行结束 , 其实就是让线程的入口方法执行完成 , 线程也就执行结束了
普通线程的入口方法就是 run 方法
主线程的入口方法就是 main 方法
那么我们想要中断线程 , 其实就只需要把线程的入口方法执行结束即可
我们有两种方法来中断线程
2.4.1 手动创建的标志位
我们可以自己设置个标志位来区分线程是否要结束
public class Demo11 {// 用一个布尔变量表示线程是否要结束// 注意使用成员变量private static boolean flag = false;public static void main(String[] args) {Thread t = new Thread(() -> {while(!flag) {System.out.println("线程运行中...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("新线程执行结束...");});t.start();try {Thread.sleep(5000);//休息5s} catch (InterruptedException e) {e.printStackTrace();}System.out.println("这里可以控制新线程退出");flag = true;}
}
这种方案是可行的 , 其实 Thread 自己就提供了这种方法 , 内置了标志位 , 就不需要我们自己创建了
2.4.2 Thread自带的标志位
获取到当前线程的实例:Thread.currentThread()
// currentThread是静态方法,'.'的方式就可以调用他的方法获取内置的标志位的值:Thread.currentThread().isInterrupted()
// 为true表示要被结束修改标志位(控制进程退出):t.interrupt();
public class Demo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while(!Thread.currentThread().isInterrupted()) {System.out.println("线程运行中~");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();Thread.sleep(5000);System.out.println("控制新线程退出");t.interrupt();}
}
我们运行之后发现 , 5s之后程序报错了 , 报错之后还在继续运行
异常是出现了 , 但是线程好像还在继续进行
这里我们就需要理解 interrupt 方法的行为 :
- 如果 t 线程没有处在阻塞状态(处在运行状态) , 此时 interrupt 就会修改内置的标志位
- 如果 t 线程处在阻塞状态 , 此时 interrupt 就会让线程内部产生异常的方法 , 例如 : interrupt 让线程里的 sleep 方法 , 抛出一个 InterruptedException 的异常.
异常被 catch 捕获了 , 但是捕获之后 , 啥也没干 , 只是打印了个调用栈
如果我们把打印栈注释掉 , 那么这次什么都不会打印
这就相当于把异常信息捕获之后 , 啥也没干 , 略过去了
正是因为这样的捕获行为 , 程序员就需要自己控制线程的退出行为了
1. 可以立即退出
public class Demo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while(!Thread.currentThread().isInterrupted()) {System.out.println("线程运行中~");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();// 1. 立即退出break;}}});t.start();Thread.sleep(5000);System.out.println("控制新线程退出");t.interrupt();}
}
2. 也可以稍后退出
public class Demo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("线程运行中~");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();// 2. 稍后退出System.out.println("新线程即将退出");try {Thread.sleep(5000);} catch (InterruptedException ex) {ex.printStackTrace();}break;}}System.out.println("新线程已经退出");});t.start();Thread.sleep(5000);System.out.println("控制新线程退出");t.interrupt();}
}
3. 就不退出 : 啥都不干 , 就是忽略了异常
主线程发出 “退出” 命令的时候 , 新线程自己决定如何处理退出的行为
比如 : 室友叫你去食堂
- 立即跟室友去食堂 [立即退出]
- 打完这局再去 [稍后退出]
- 装没听见 [不去退出]
另外我们在定义的部分是这样说的 :
如果 t 线程处在阻塞状态 , 此时 interrupt 就会让线程内部产生阻塞的方法
他的意思就是 interrupt 不会自己抛出异常 , 他会在中间捣乱 , 让线程内部的 sleep 抛出异常
除了上述方法 , 判定标志位还有另一种方法 : Thread.interrupted()
这种方法的标志位会自动清除
比如 : 刚开始的时候 , 我们去控制他中断 , 这里的标志位先设为 true
等到读取的时候会读到这个 true , 但是读取完之后这个标志位就自动恢复成 false 了
就类似于开关 : 开关一按 , 自己就又弹起来了
而我们上面讲的 Thread.currentThread().isInterrupted()
就是开关按下去 , 就不弹起来了
我再帮大家梳理一下思路
2.5 线程等待 join
2.5.1 join 是怎么回事?
我们知道 , 线程之间的执行顺序是完全随机的 , 是由系统决定的 .
但是我们对于这种随机性的东西很头疼 , 所以让顺序能够确定下来 , join 关键字就应运而生.
join 就是一种确定线程执行顺序的辅助手段
咱们虽然不能决定多个线程开始的顺序 , 但是这回有了 join , 我们就可以决定结束的顺序了.
还是看一下我们之前的代码
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();}
}
在这里面 , main t1 t2 三个线程是随机调度执行的
那么此处的需求 , 就是希望让 t1 和 t2 都执行完了之后 , main 再开始计时 , 来统计执行时间
也就是让 t1 和 t2 先执行完 , main 后执行完
虽然我们无法干预调度器的行为 , 但是我们可以控制 main 线程主动的去进行等待 , 所以我们在 main 线程中分别调用了 t1.join() [main 阻塞 , 等待 t1 执行完]、t2.join() [main 阻塞 , 等待 t2 执行完]
当 t1 t2 都执行完毕 , main 解除阻塞 , 然后程序继续向下执行 , 才能执行完
可以类比领导与职工 , 我们的 main 线程就是 领导 , 我们的 t1 线程和 t2 线程就是员工
早上上班 , 领导给两位职工派活
接下来 , 领导就一直摸鱼 , 等待 t1 和 t2 汇报成果
直到 t1 和 t2 完成工作 , main 线程拿过来验收 , 整个流程才算完事
谁先调用 join 谁后调用 join 都是无所谓的
我们想让谁阻塞 , 谁就调用 join 即可
比如我们想让 t1 以及 t2 阻塞
我们就在 main 线程中调用 t1.join() t2.join() 即可
但是 main 线程比较特殊 , 没有这样的写法 main.join()
, 剩下其他自己创建的线程 , 都可以 join
另外 , join 自身也是有可能发生阻塞的 , 所以我们也要去处理一下异常
try {t1.join();t2.join();
} catch (InterruptedException e) {e.printStackTrace();
}
join 的行为 :
- 如果被等待的线程还没执行完 , 就阻塞等待
- 如果等待的线程已经执行完了 , 直接就返回
2.5.2 栗子1 : 让 main 等待 t2 , t2 去等待 t1
让 main 调用 t2.join() , 让 t2 调用 t1.join()
public class Demo13 {//先创建两个线程,但是不指向任何元素,方便后面访问private static Thread t1 = null;private static Thread t2 = null;public static void main(String[] args) throws InterruptedException {System.out.println("main开始");t1 = new Thread(() -> {System.out.println("t1开始");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t1结束");});t1.start();t2 = new Thread(() -> {System.out.println("t2开始");try {t1.join();//让t2等待t1执行完再去执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t2结束");});t2.start();t2.join();//main等待t2System.out.println("main结束");}
}
2.5.3 栗子2 : 控制 main 先运行 t1 , t1 执行完再运行 t2
实际上就是谁后调用 join , 谁就后结束
所以先调用 t1.join(), 后调用 t2.join() 就代表让 t1 先执行完 , t2 后执行完 , 就达到我们想要的结果了
public class Demo10 {public static void main(String[] args) throws InterruptedException {System.out.println("main 开始");Thread t1 = new Thread(() -> {System.out.println("t1 开始");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t1 结束");});t1.start();t1.join();Thread t2 = new Thread(() -> {System.out.println("t2 开始");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t2 结束");});t2.start();t2.join();System.out.println("main 结束");}
}
2.5.4 带参数版本的 join
函数 | 作用 |
---|---|
public void join() | 死等线程结束 (不见不散) |
public void join(long mills) | 设定了最大等待时间 |
public void join(long mills,int nanos) | 传了更加精确的时间 |
未来实际开发程序的时候 , 像这种的等待操作 , 一般不会采用死等的方式 , 这种方式有风险 ! 万一代码出了 bug 没控制好 , 死等就容易让服务器卡死 , 无法继续工作 .
其中 , public void join(long mills,int nanos)
这个方法 , 第一个参数传的是毫秒 , 第二个参数传的是纳秒 , 比如第一个参数是 100 , 第二个参数是 1000 , 相当于等待了 100.001 ms
2.6 获取到线程引用 : Thread.currentThread()
为了对线程进行操作(线程等待、线程中断、获取各种线程的属性…) , 就需要获取到线程引用
这个方法我们刚才已经用到了
Thread.currentThread();
在线程操作之前 , 我们需要先获取到线程的引用 , 才能对线程进行具体操作
如果是继承 Thread , 然后重写 run 方法的话 , 可以直接在 run 中使用 this 即可获取到线程的实例
但是如果是 Runnable 或者 lambda , this 的方式就不可以了 , 这两种情况下 , this 就不是指向 Thread 实例
所以更通用的办法就是使用 Thread.currentThread()
, 这是一个静态方法 , 哪个线程调用这个方法 , 得到的结果就是哪个线程的实例 , 以后获取到线程实例 , 我们就无脑使用这个方法即可
2.7 线程的休眠 : sleep
sleep 能让线程休眠一会
我们先讲解一下 相关的原理
那么关于 PCB , 由于我们之前只介绍了进程 , 但是现在新学了线程 , PCB 这个概念还是需要重新提一下的
一组进程包含多组线程 , 每个线程都有自己的 PCB , 那么每一组进程其实就是对应一组 PCB 了
但是同一个进程里面的若干 PCB 还是有关联的
- PCB里面有一个 “线程组ID” , 是一样的
- PCB 里面的内存指针和文件描述符表 , 都是一样的
三 . 线程的状态
我们之前介绍过就绪状态与阻塞状态 , 在 Java / JVM 中 , 对于线程的状态 , 做了一个更明确的区分
NEW: 安排了工作, 还未开始行动
RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
BLOCKED: 这几个都表示排队等着其他事情
WAITING: 这几个都表示排队等着其他事情
TIMED_WAITING: 这几个都表示排队等着其他事情
TERMINATED: 工作完成了
3.1 通过代码来看线程的状态
public class Demo13 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {System.out.println("Hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});// NEW 状态:// 在 start 之前获取// 获取到的是线程还未创建的状态System.out.println(t.getState());t.start();// RUNNABLE 状态// 线程就绪状态// 线程正在工作中System.out.println(t.getState());// TIMED_WAITING 状态// 线程调用了 sleep 方法Thread.sleep(500);System.out.println(t.getState());t.join();// 等待t线程结束// TERMINATED 状态// 在 join 之后获取// 获取到的是线程已经结束后的状态System.out.println(t.getState());}
}
3.2 线程状态和状态转移的意义
这个图看起来吓人 , 实际上我们可以简化成下面这样
我们再给大家简单介绍一个方法 : yield
yield 的作用是让调用者暂时放弃CPU , 回到阻塞队列里面去排队
我们可以粗暴的理解成 sleep(0)
用的不多 , 有印象即可
关于 yield , 举个栗子 :
我跟我的妈妈去超市 , 到结账的位置了 , 她突然想起来 “艾玛 , 忘打酱油了” , 让我继续排队 , 但是我都要交钱了她还没回来 , 这就很尴尬 . 那么 yield 就是我让后面结账的那个人先结账 , 我的妈妈要是还没回来 , 那就再让后面的人继续往前去结账 , 这就相当于我们一直没出就绪队列 . sleep(0)的意思就是我觉得不好意思了 , 刚退出队伍 , 我妈妈就赶回来了 , 我又赶紧回去队伍 , 这就是出了阻塞队列然后又立马回来了
yield 就是短暂的放弃 CPU , 排到就绪队列中的后面位置 (还是在就绪队列中 , 没进阻塞队列)
slepp(0) 就是马上进入阻塞队列 , 又马上回就绪队列 , 效果和 sleep 类似
相关文章:

Java Web 实战 03 - 多线程基础(2)
Java Web 实战 03 - 多线程基础篇 2二 . Thread类常见方法2.1 Thread 的常见构造方法2.2 Thread 的几个常见属性getId()getName()getState()getPriority()isDaemon()案例 : 实现 getId()、getName()、 getState()、getPriority()、isDaemon()、isAlive()2.3 启动一个线程-start…...

Linux命令·cat
cat命令的用途是连接文件或标准输入并打印。这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用。 1.命令格式:cat [选项] [文件]...2.命令功…...

WPF WrapPanel、UniformGrid、DockPanel介绍
WPF WrapPanel、UniformGrid、DockPanel介绍 WrapPanel WrapPanel , 具有在有限的容器范围内, 可以自动换行, 或者换列处理。具体则取决于WrapPanel的排列方式 (Orientation)。 Orientation"Horizontal"时各控件从左至右罗列,当面板长度不够时ÿ…...

华为OD机试题 - TLV 编码(JavaScript)| 含思路
华为OD机试题 最近更新的博客使用说明本篇题解:TLV 编码题目输入输出描述示例一输入输出说明Code解题思路华为OD其它语言版本最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流…...

【华为OD机试真题java、python、c++】开心消消乐【2022 Q4 100分】(100%通过)
代码请进行一定修改后使用,本代码保证100%通过率。本文章提供java、python、c++三种代码 题目描述 给定一个N行M列的二维矩阵,矩阵中每个位置的数字取值为0或1。矩阵示例如: 1100 0001 0011 1111 现需要将矩阵中所有的1进行反转为0,规则如下: 1) 当点击一个1时,该1便被…...

IDEA搭建vue-cli | vue-router | 排错思路、Webpack、Axios、周期、路由、异步、重定向
💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Vue.js概述 Vue 是一套用于构建用户界面的渐进式JavaScript框架。 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层…...

HashSet原理
HashSet原理HashSet原理1.概述2.底层代码3.原理图解4.总结4.1: 1.7原理总结4.2: 1.8原理总结HashSet原理 1.概述 HashSet 实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的 迭代顺序;特别是它不保证…...

【C#进阶】C# 特性
序号系列文章10【C#基础】C# 正则表达式11【C#基础】C# 预处理器指令12【C#基础】C# 文件与IO文章目录前言1,特性的概念1.1 特性的属性1.2 特性的用途2,特性的定义2.1 特性参数2.2 特性目标3,预定义特性3.1 AttributeUsage3.2 Conditional3.2…...

Java速成篇-Day01笔记
提示:这里只记录我个人不熟悉的知识,并非所有内容 笔记目录课程:04-第一行代码① jshell② 对象.方法课程:05-第一份源码① Java开发程序的流程② 入口方法课程:06-常见问题-中文乱码① 乱码原因② 解决方法课程&#…...

从源码开始精通spring-security1
参考b站up主:传送门 前沿: 本章:spring-security 重要的成员 WebSecurity、HttpSecurity、SecurityBuilder、SecurityFilterChain、FilterChainProxy 重点:WebSecurity、HttpSecurity 他们都实现了 SecurityBuilder 接口 用来构建对象 WebSe…...

你应该优化的JavaScript代码,以及前端工程师日常使用的小技巧。使之更加简洁,可读性更强,更易维护。
本文主要是分享一下平时前端工程师,在写前端代码过程中的一些代码优化,以及使用的一些小技巧,来使我们的代码更加简洁,可读性更强,更易维护。 1. 字符串的自动匹配(includes的优化) includes是…...

自动化测试
文章目录前言一、什么是自动化测试?一个简单的自动化实例二、自动化测试的分类1.接口自动化测试2.UI自动化测试(界面测试)移动端自动化测试web端自动化测试(主要学习)三、selenium工具1.为什么选择selenium作为web自动…...

leetcode-每日一题-807(中等,数组)
正常情况第一眼看这道题,看懂意思的话很简单就可以解出来。给你一座由 n x n 个街区组成的城市,每个街区都包含一座立方体建筑。给你一个下标从 0 开始的 n x n 整数矩阵 grid ,其中 grid[r][c] 表示坐落于 r 行 c 列的建筑物的 高度 。城市的…...

【Linux】Linux项目自动化构建工具make makefile
文章目录1. 背景2.实例3.原理4.项目清理5. 文件属性中的三个时间6. Linux下第一个小程序——进度条6.1 前置知识1:缓冲区6.2前置知识2:回车换行6.3进度条的实现7 Linux下git的”三板斧“1. 背景 一个工程中的源文件不计其数,其按类型、功能、…...

华为OD机试题 - IPv4 地址转换成整数(JavaScript)| 含思路
华为OD机试题 最近更新的博客使用说明本篇题解: IPv4 地址转换成整数题目输入输出示例一输入输出说明示例一输入输出说明Code解题思路华为OD其它语言版本最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | …...

spring整合通用mapper
1.使用通用mapper可以更快速的进行数据库的增删查改操作,加入springboot的管理,常规的SQL都可以复用 2.整合 a)引入依赖 <dependencies><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId>…...

一天什么时间发抖音浏览量高?5个抖音最佳发布时间段
抖音作为现在一款现象级的软件,已经不知不觉地影响着我们生活的方方面面。那抖音想要被更多人看到,就需要掐准哪些时间活跃数最多,今天就来和大家分享一下一天什么时间发抖音浏览量高,又该如何抓住最佳投放契机呢?一、一天什么时…...

华为OD机试题 - 关联子串(JavaScript)| 含思路
华为OD机试题 最近更新的博客使用说明本篇题解:关联子串题目输入输出示例一输入输出说明示例二输入输出说明Code解题思路华为OD其它语言版本最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典...

【代码随想录训练营】【Day33休息】【Day34】第八章|贪心算法|1005.K次取反后最大化的数组和|134. 加油站|135. 分发糖果
K 次取反后最大化的数组和 题目详细:LeetCode.1005 这道题比较简单,这里直接给出贪心策略: 局部最优解: 按照 负数 > 0 > 正数 的优先级次序,依次对nums中的较小数值进行取反因为负负得正,负值越小…...

<c++> const 常量限定符
文章目录什么是 const 常量限定符const 的初始化const 的默认作用域const 的引用例外情况const 与指针const指针的声明指向 const 的指针const指针指向 const 的 const指针什么是 const 常量限定符 Q:什么是 const 常量限定符? A:const名叫常…...

pytorch实现transformer模型
Transformer是一种强大的神经网络架构,可用于处理序列数据,例如自然语言处理任务。在PyTorch中,可以使用torch.nn.Transformer类轻松实现Transformer模型。 以下是一个简单的Transformer模型实现的示例代码,它将一个输入序列转换为…...

【懒加载数据 Objective-C语言】
一、咱们就开始进行懒加载 1.懒加载发现,每一个字典,是不是就是四个键值对组成的: 1)answer:String,中国合伙人, 2)icon:String,movie_zghhr, 3)title:String,创业励志电影, 4)options:Array,21 items 前三个都是String类型,最后是不是Array类型, 所…...

人脸网格/人脸3D重建 face_mesh(毕业设计+代码)
概述 Face Mesh是一个解决方案,可在移动设备上实时估计468个3D面部地标。它利用机器学习(ML)推断3D面部表面,只需要单个摄像头输入,无需专用深度传感器。利用轻量级模型架构以及整个管道中的GPU加速,该解决…...

JMeter 控制并发数
文章目录一、误区二、正确设置 JMeter 的并发数总结没用过 JMeter 的同学,可以先过一遍他的简单使用例子 https://blog.csdn.net/weixin_42132143/article/details/118875293?spm1001.2014.3001.5501 一、误区 在使用 JMeter 做压测时,大家都知道要这么…...

git常用命令汇总
Git 是一种分布式版本控制系统,它具有以下优点: 分布式:每个开发者都可以拥有自己的本地代码仓库,不需要连接到中央服务器,这样可以避免单点故障和网络延迟等问题。 非线性开发:Git 可以支持多个分支并行开…...

【2023】华为OD机试真题Java-题目0226-寻找相似单词
寻找相似单词 题目描述 给定一个可存储若干单词的字典,找出指定单词的所有相似单词,并且按照单词名称从小到大排序输出。单词仅包括字母,但可能大小写并存(大写不一定只出现在首字母)。 相似单词说明:给定一个单词X,如果通过任意交换单词中字母的位置得到不同的单词Y,…...

【项目管理】晋升为领导后,如何开展工作?
兵随将转,作为管理者,你可以不知道下属的短处,却不能不知道下属的长处。晋升为领导后,如何开展工作呢? 金九银十,此期间换工作的人不在少数。有几位朋友最近都换了公司,职位得到晋升,…...

JAVA开发(Spring Gateway 的原理和使用)
在springCloud的架构中,业务服务都是以微服务来划分的,每个服务可能都有自己的地址和端口。如果前端或者说是客户端直接去调用不同的微服务的话,就要配置不同的地址。其实这是一个解耦和去中心化出现的弊端。所以springCloud体系中࿰…...

踩坑:解决npm版本升级报错,无法安装node-sass的问题
npm版本由于经常更新,迁移前端项目时经常发现报错安装不上。 比如,项目经常使用的sass模块,可能迁移的时候就发现安装不了。 因为node-sass 编译器是通过 C 实现的。在 Node.js 中,采用 gyp 构建工具进行构建 C 代码,…...

xFormers安装使用
xFormers是一个模块化和可编程的Transformer建模库,可以加速图像的生成。 这种优化仅适用于nvidia gpus,它加快了图像生成,并降低了vram的使用量,而成本产生了非确定性的结果。 下载地址: https://github.com/faceb…...