【JAVA CORE_API】Day19 多线程API(2)、多线程并发安全问题、同步
多线程API
进程和线程
-
进程:进程就像是一个程序在电脑里运行时的一个实例。你可以把它想象成一个独立的小工人,专门负责完成某项任务(比如打开浏览器、播放音乐)。每个进程都有自己独立的资源(比如内存)和工作空间,不会轻易打扰其他进程。多个进程可以同时运行,各自处理自己的任务。

-
线程:线程就像是一个进程中的小工人。一个进程可以有多个线程,每个线程负责进程中的某个具体工作,比如在一个浏览器里同时加载不同的网页。线程之间可以共享进程的资源(比如内存),所以它们可以更快地协同工作。但因为它们共享资源,也需要小心不要互相干扰。

进程和线程的关系
-
进程和线程的关系就像是公司和员工的关系。一个进程是公司,它可以有多个员工(线程)一起工作。每个员工都有自己的任务,但他们共享公司的资源(比如办公设备)。
-
进程切换是指CPU从一个公司(进程)跳到另一个公司,执行不同公司的任务。因为每个公司有自己的资源,所以这个切换需要花费时间去保存和加载这些资源。
-
线程切换是指CPU在同一个公司内换员工执行任务。因为员工共享公司的资源,切换时不需要太多额外操作,所以速度更快。
- CPU通过快速切换进程和线程来处理多任务,确保每个任务都能得到执行。
进程与线程的区别
-
资源占用:
-
进程:进程是独立的,拥有自己完整的资源集,包括内存、文件句柄等。每个进程有自己独立的地址空间,这使得进程之间相对隔离,互不干扰。
-
线程:线程是进程内的一个执行单元,多个线程共享同一个进程的资源(如内存、文件句柄)。因此,线程之间的资源共享更直接、效率更高,但也更容易引发资源争用的问题。

-
-
开销:
-
进程:进程的创建、销毁和切换都需要较大的开销。因为进程切换涉及到保存和恢复独立的资源(如内存状态、CPU寄存器等),所以会消耗更多的系统资源和时间。
-
线程:线程的创建、销毁和切换开销较小。因为线程共享进程的资源,切换时只需保存和恢复少量的线程上下文,所以切换速度更快,系统资源占用更少。
-
-
并发性:
-
进程:进程之间的并发性较弱,因为它们彼此独立,不容易直接共享数据。要实现进程间的通信(IPC),通常需要借助系统提供的机制(如管道、消息队列等),这会增加复杂性和开销。
-
线程:线程之间的并发性较强。由于线程共享进程的资源,多个线程可以更容易地协同工作、直接共享数据,从而提高并发执行的效率。但同时,这也容易引发线程间的同步问题,如数据竞争和死锁。
-
线程的生命周期
-
新建(New):线程对象被创建,但还没有开始执行。就像一个人刚出生,刚有了生命,但还没有真正开始行动。
-
就绪(Runnable):线程已经准备好执行,等待CPU调度。就像人长大到可以学习和工作的年龄,准备好去做事,等待机会开始行动。
-
运行(Running):线程获得CPU时间片,开始执行任务。就像当人获得工作机会或任务,开始真正行动,比如开始学习、工作。
-
阻塞(Blocked):线程因等待某些资源(如锁、IO)而暂停执行,直到资源可用。就像人遇到困难或等待某些条件(比如等材料、等别人回应),暂时不能继续工作,只能等待问题解决。
-
终止(Terminated):线程执行完毕或因异常退出,生命周期结束。就像人完成任务或退休,生命结束,所有活动停止。

线程优先级
-
线程优先级是一个数值,用来决定线程获取CPU时间的先后顺序。优先级越高,线程越有可能先被执行。
-
线程切换方式:操作系统根据线程优先级和调度算法,在多个线程间切换,分配CPU资源。
-
设置线程优先级:通过
setPriority(int newPriority)方法设置,优先级范围从1(最低)到10(最高)。 -
默认优先级:所有线程默认优先级为5,即中等优先级。
-
-
优先级只是一个建议,具体调度由操作系统决定。
-
Java 中有三个线程优先级常量,用于方便地设置线程的优先级:
-
Thread.MIN_PRIORITY:最小优先级,值为1。适用于不紧急的任务。 -
Thread.NORM_PRIORITY:普通优先级,值为5。是默认优先级,适用于一般任务。 -
Thread.MAX_PRIORITY:最大优先级,值为10。适用于需要优先处理的任务。
-
package day19;/*** 线程的优先级* 线程有10个优先级,分别用整数1-10表示,其中1是最低的,10是最高的,5是默认值*/public class PriorityDemo {public static void main(String[] args) {Thread thread01 = new Thread(){@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("Min" + i);}}};Thread thread02 = new Thread(){@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("Normal:" + i);}}};Thread thread03 = new Thread(){@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("Max:" + i);}}};thread01.setPriority(Thread.MIN_PRIORITY);thread02.setPriority(Thread.NORM_PRIORITY);thread03.setPriority(Thread.MAX_PRIORITY);thread01.start();thread02.start();thread03.start();}}
sleep阻塞
sleep阻塞是指线程调用Thread.sleep(milliseconds)方法后,进入阻塞状态,暂停执行指定的时间。
-
作用:
sleep方法用于让当前线程暂停执行,释放CPU,让其他线程有机会运行。 -
特点:线程在
sleep期间保持着它的资源(如锁),只是暂时停止运行。当时间到达后,线程自动恢复到就绪状态,等待CPU再次调度。 -
注意:
sleep不会释放锁资源,也不会影响线程的优先级。它只是简单地让线程暂停一段时间。sleep阻塞常用于控制线程执行的节奏,比如定时任务或轮询操作。package day19;/*** Thread提供了一个方法:static void sleep(long ms)* 暂停当前线程,让出CPU,让出时间片,等待ms毫秒* 超时后会自动回到Runnable状态再次开始并发*/public class SleepDemo {public static void main(String[] args) {System.out.println("程序开始...");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("程序结束了...");}}-
在实际使用情况下,sleep()有以下几种常见的用途:
-
控制任务执行频率:使用
sleep()来暂停线程,使其每隔一定时间执行任务。例如,定时检查某个条件或定期更新状态。 -
模拟延迟:在开发和测试过程中,使用
sleep()模拟网络延迟或处理时间,以测试系统对延迟的反应。 -
避免资源过度占用:在高频率的任务(如轮询)中,使用
sleep()减少对CPU的过度占用,降低系统负载。 -
实现简单的等待机制:在多线程程序中,线程可能需要等待某些条件满足后才能继续执行。使用
sleep()可以实现简单的等待或延时。 -
调试和测试:在调试和测试中,
sleep()可用于创建特定的时间间隔,以便观察程序的行为或结果。
-
-
我们来写一个定时器Demo,需求:程序运行后在控制台上输入一个数字,然后从该数字开始每秒递减,到0时输出时见到。
package day19;import java.util.Scanner;public class ClockDemo {public static void main(String[] args) {System.out.println("请输入一个数字:");Scanner scanner = new Scanner(System.in);System.out.println("倒计时开始!");for (int i = scanner.nextInt(); i >= 0; i--) {System.out.println(i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("时间到!");}}
-
InterruptedExcption阻塞中断异常
-
InterruptedException是 Java 中的一个异常,表示线程在阻塞状态(如sleep()、wait()或join())时被中断了。 -
在
sleep()方法中的作用:-
当线程调用
sleep()方法进入阻塞状态时,如果在等待期间有其他线程调用interrupt()方法中断这个线程,sleep()方法会抛出InterruptedException。 -
这允许线程在被中断时可以立即恢复执行,并处理中断请求。
-
-
使用场景:
-
响应中断请求:在长时间运行的线程中,使用
sleep()时可能会有其他线程请求中断。此时,InterruptedException让线程能够响应中断请求,做出适当的处理(如清理资源、退出循环)。 -
中断支持的任务:在需要支持中断的任务中,比如一个线程正在等待或处理任务,但也需要能够中断以快速终止任务或恢复其他操作,使用
sleep()时应考虑InterruptedException。
-
-
实际开发中,处理
InterruptedException是重要的,特别是在多线程程序中,确保线程能够响应中断请求并适当地退出或清理资源。-
示例:
package day19;/*** 线程阻塞打断异常*/public class InterruptedExceptionDemo {public static void main(String[] args) {Thread thread = new Thread("美女"){@Overridepublic void run() {try {System.out.println(getName() + ":刚美完容,睡一觉吧...zZ");Thread.sleep(100000000);} catch (InterruptedException e) {System.out.println(getName() + ":干嘛呢?干嘛呢!干嘛呢!!!");}}};Thread thread1 = new Thread("装修工") {@Overridepublic void run() {System.out.println(getName() + ":大锤80!小锤40!大哥你要几锤?");for (int i = 0; i < 5; i++) {System.out.println(getName() + ":80!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("咣当!");System.out.println(getName() + ":大哥!搞定!!!");thread.interrupt();}};thread.start();thread1.start();}}
-
守护线程
-
什么是守护线程
守护线程(Daemon Thread)是指在后台运行的线程,它为其他线程提供服务,但不会阻止程序退出。守护线程通常用于执行不重要的任务,如垃圾回收、后台监控等。
-
守护线程和普通线程的区别
-
生命周期:
-
守护线程:当所有用户线程(非守护线程)全部结束时,守护线程才会结束。它不会阻止程序退出。
-
普通线程:程序的退出会等到所有普通线程都结束后,程序才会终止。
-
-
优先级:
-
守护线程:通常优先级较低,因为它的任务是辅助性质的。
-
普通线程:优先级可以根据任务的重要性设定。
-
-
-
如何使用守护线程
在创建线程时,可以通过
Thread类的setDaemon(true)方法将其设为守护线程。例如:void setDeamon(boolean) // 当参数为true时该线程为守护线程 -
守护线程的特点
-
后台服务:守护线程通常用于后台服务任务,如垃圾回收、日志记录、监控等。
-
自动退出:守护线程不会阻止程序终止,当所有非守护线程结束时,守护线程也会结束。
-
-
守护线程与垃圾回收(GC)
守护线程通常用于执行垃圾回收任务。Java 的垃圾回收线程就是一种守护线程,它在后台运行,清理不再使用的对象,而不会阻止程序的终止。
-
守护线程在什么情况使用
-
后台任务:适用于需要在后台持续运行的任务,而这些任务不应该阻止程序退出。例如,后台监控、定时任务、后台日志记录等。
-
辅助服务:适用于提供服务或支持其他用户线程的任务,比如线程池中的工作线程通常是守护线程。
-
package day19;public class DaemonThreadDemo {public static void main(String[] args) {// Rose线程Thread threadRose = new Thread("Rose"){@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(getName() + ":Let me go!!!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(getName() + ":AAAAAAAAAA!!!!");System.out.println("噗通~");}};// Jack线程Thread threadJack = new Thread("Jack"){@Overridepublic void run() {while (true) {System.out.println(getName() + ":You jump! I jump!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};// 线程控制threadRose.start();threadJack.setDaemon(true); // 将threadJack设置为守护线程threadJack.start();}}
多线程
多线程并发安全
-
生活中的例子
-
共享银行账户:
-
场景:假设有一个银行账户,两个不同的客户同时使用自动取款机(ATM)和柜台取款同一账户,每个客户就是一个线程,如果CPU执行客户1取款线程的一半时间片结束将线程分配给客户2,检查余额时恰巧同时进入检查余额是否足够的阶段,会不会出现都返回true的情况发生?两个人都成功取款?我们看下面的代码:
package day19;/*** 当多个线程并发操作同一临界资源,由于线程切换实际不确定,导致执行顺序出现混乱而产生不良后果,这就是线程并发安全问题** 临界资源:操作该资源的过程同时只能被单个线程进行*/public class SyncDemo01 {static boolean success1 = false; // 表示第一个人是否取款成功static boolean success2 = false; // 表示第二个人是否取款成功public static void main(String[] args) {int sum = 0; // 记录测试的次数Bank bank = new Bank();while (true) {sum++;Thread thread1 = new Thread() {@Overridepublic void run() {success1 = bank.getMoney(20000); // 取款}};Thread thread2 = new Thread() {@Overridepublic void run() {success2 = bank.getMoney(20000); // 取款}};thread1.start();thread2.start();try {Thread.sleep(10); // 阻塞主线程一段时间,用于让t1,t2线程执行完毕if (success1 && success2) {System.out.println("成功!两个人都取出20000块!");break;} else {System.out.println("失败!正常取款,只有一人取款成功!");success1 = false;success2 = false;bank.saveAccount(20000);}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("第" + sum + "次测试:");}}}class Bank {private int account = 20000; // 银行余额// 取钱方法,返回true说明允许出钞,返回false说明不允许出钞public boolean getMoney(int money) {int account = getAccount(); // 查询余额方法if (account >= money) { // 判断余额是否足够account = account - money; // 扣款Thread.yield(); // 主动放弃当前线程的时间片,模拟到这里CPU恰好分配的时间片结束saveAccount(account); // 保存新余额return true; // 允许出钞}return false; // 不允许出钞}public void saveAccount(int account){this.account = account;}public int getAccount() {return account;}} -
并发安全:为了防止两个客户取款同时修改账户余额导致不一致,银行系统需要使用机制(如锁)来确保每次只有一个客户能够修改账户余额,从而保证账户余额的准确性。
-
-
-
计算机相关的例子
-
线程安全的计数器:
-
场景:多个线程同时增加一个全局计数器的值。如果没有适当的同步机制,多个线程同时对计数器进行操作可能会导致计数器值的丢失或重复增加。
-
并发安全:为了确保计数器的正确性,通常会使用线程安全的数据结构(如
AtomicInteger)或在增加计数器时使用同步机制(如synchronized关键字或Lock)来保证每次操作的原子性,从而避免数据竞争。
-
-
如何解决并发问题
-
同步(Synchronization)
同步在多线程编程中是指协调多个线程对共享资源的访问,以防止并发操作导致数据的不一致性。由于多个线程可能同时访问和修改共享数据,如果没有同步机制,就会导致数据竞争和不可预测的结果。
-
synchronized关键字Java 提供了
synchronized关键字来实现同步,确保同一时刻只有一个线程可以执行被synchronized修饰的代码部分,从而保护共享资源。 -
synchronized的两种用法:-
同步方法:
-
当一个方法被
synchronized修饰时,线程在调用这个方法之前需要获得方法所属对象的锁(如果是静态方法,则是类对象的锁)。 -
当一个线程持有对象锁时,其他线程必须等待,直到这个锁被释放后才能访问这个同步方法。
public synchronized void exampleMethod() {// 同步代码 } -
-
同步代码块:
-
通过
synchronized关键字可以对一个特定的对象加锁,通常是this,以确保某一代码块在同一时间只能被一个线程执行。 -
这种方式比同步整个方法更灵活,只锁住关键的代码部分,可能减少不必要的性能损失。
public void exampleMethod() {synchronized(this) {// 同步代码块}} -
-
-
优化刚才的代码:
package day19;/*** 当多个线程并发操作同一临界资源,由于线程切换实际不确定,导致执行顺序出现混乱而产生不良后果,这就是线程并发安全问题** 临界资源:操作该资源的过程同时只能被单个线程进行*/public class SyncDemo01 {static boolean success1 = false; // 表示第一个人是否取款成功static boolean success2 = false; // 表示第二个人是否取款成功public static void main(String[] args) {int sum = 0; // 记录测试的次数Bank bank = new Bank();while (true) {sum++;Thread thread1 = new Thread() {@Overridepublic void run() {success1 = bank.getMoney(20000); // 取款}};Thread thread2 = new Thread() {@Overridepublic void run() {success2 = bank.getMoney(20000); // 取款}};thread1.start();thread2.start();try {Thread.sleep(10); // 阻塞主线程一段时间,用于让t1,t2线程执行完毕if (success1 && success2) {System.out.println("成功!两个人都取出20000块!");break;} else {System.out.println("失败!正常取款,只有一人取款成功!");success1 = false;success2 = false;bank.saveAccount(20000);}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("第" + sum + "次测试:");}}}class Bank {private int account = 20000; // 银行余额// 取钱方法,返回true说明允许出钞,返回false说明不允许出钞public synchronized boolean getMoney(int money) {int account = getAccount(); // 查询余额方法if (account >= money) { // 判断余额是否足够account = account - money; // 扣款Thread.yield(); // 主动放弃当前线程的时间片,模拟到这里CPU恰好分配的时间片结束saveAccount(account); // 保存新余额return true; // 允许出钞}return false; // 不允许出钞}public void saveAccount(int account){this.account = account;}public int getAccount() {return account;}} -
这段代码还存在性能问题:原因是把
synchronized锁放在了整个getMoney()方法上。虽然这样可以解决线程并发安全问题,但会导致性能下降,因为它限制了同一时间只有一个线程能够访问整个方法,这在多线程环境中会降低系统的吞吐量。 -
生活中的类比:去商场买衣服,想象你去商场买衣服的过程:
-
全程锁定:如果商场为了避免拥挤,每次只允许一个顾客进入并从头到尾完成所有购物步骤(挑选、试穿、付款等),这样可以确保秩序,但效率非常低,其他顾客必须等待很长时间才能轮到自己。
-
部分锁定:更合理的做法是只在关键步骤上(比如付款时)进行控制,同一时间只允许一个顾客在收银台结账,而其他顾客可以同时进行挑选和试穿。这样可以大大提高整体效率。
-
-
如何解决锁带来的性能问题呢?
同步块
-
同步块的组成和语法
同步块由
synchronized关键字和一个同步监视器对象(锁)组成,确保在同一时间只有一个线程可以执行同步块内的代码。 -
语法:
java复制代码synchronized(锁对象) {// 需要同步的代码} -
同步监视器对象的选取
-
锁对象是同步块的关键,它用于控制访问权限。
-
常见选择:
-
this:当前对象实例,用于实例方法的同步。 -
类对象:
ClassName.class,用于静态方法的同步。 -
任意其他对象:用于细粒度控制,避免锁定过多代码。
-
package day19;public class SyncDemo02 {public static void main(String[] args) {shop shop = new shop();Thread thread01 = new Thread("张禹垚一号") {@Overridepublic void run() {shop.buy();}};Thread thread02 = new Thread("张禹垚二号") {@Overridepublic void run() {shop.buy();}};thread01.start();thread02.start();}}class shop {public void buy(){try {Thread thread = Thread.currentThread(); // 获取当前线程对象System.out.println(thread.getName() + ":正在购买商品...");Thread.sleep(5000);synchronized (this) {System.out.println(thread.getName() + ":正在试衣服...");Thread.sleep(5000);}System.out.println(thread.getName() + ":购买成功!");} catch (InterruptedException e) {e.printStackTrace();}}} -
-
选择合适的锁:
-
锁粒度:锁的范围应尽量小,以减少线程争用。选择只需要保护共享资源的关键部分作为锁对象。
-
实例锁:用
this作为锁对象,适用于保护当前实例的共享资源,确保同一实例的同步方法或代码块同时只能被一个线程访问。 -
类锁:用
ClassName.class作为锁对象,适用于保护静态变量或方法,确保同类的所有实例共享同一把锁。 -
自定义锁:用特定的对象作为锁,适用于需要更灵活的同步控制,避免过多的竞争。选择与共享资源最相关的对象。
-
一致性:确保所有相关代码段使用同一个锁对象,防止线程间产生不一致的锁定。
-
相关文章:
【JAVA CORE_API】Day19 多线程API(2)、多线程并发安全问题、同步
多线程API 进程和线程 进程:进程就像是一个程序在电脑里运行时的一个实例。你可以把它想象成一个独立的小工人,专门负责完成某项任务(比如打开浏览器、播放音乐)。每个进程都有自己独立的资源(比如内存)和…...
最新Windows 11 23H2精简版,免费获取!稳定流畅!
今日,系统之家小编给大家带来了2024最新的Windows11 23H2精简版系统,该版本系统经过适度地优化与精简,保留大部分功能,完全能满足日常使用需求,兼容性非常出色,无需担心应用程序出现闪退问题。大家可以通过…...
PostgreSQL SELECT 语句:深入解析与实例应用
PostgreSQL SELECT 语句:深入解析与实例应用 PostgreSQL 是一款功能强大的开源关系数据库管理系统,它以稳定性、可靠性以及支持高级功能而著称。在 PostgreSQL 中,SELECT 语句是最基本也是最重要的查询语句之一,用于从数据库表中检索数据。本文将详细介绍 SELECT 语句的用…...
【自然语言处理】 构建文本对话系统
构建文本对话系统的框架如下: 根据聊天系统目的功用的不同,可分成三大类型: 闲聊式机器人:较有代表性的有微软小冰、微软小娜、苹果的 Siri、小 i 机器人等,主要以娱乐为目的。 **知识问答型机器人:**知识…...
java: 程序包org.slf4j不存在
当在Java项目中遇到“程序包org.slf4j不存在”的错误时,这通常意味着你的项目没有正确地包含SLF4J(Simple Logging Facade for Java)的库。SLF4J是一个Java的日志门面(Facade),它允许你在后端使用不同的日志…...
图片转PDF怎么转?教你3种快捷方便的jpg转pdf方法
图片文件以及PDF文档已经是我们工作当中不可或缺的一部分,我们在一些商务合作的场景下经常需要把拍摄下来的合同、企划书、画册等图片内容转换为PDF格式后再发送,这样能够极大程度的保证文件的安全性,那么图片应该如何转换成PDF文件呢?今天来…...
数据防泄密软件如何防止数据泄密?七大措施筑起数据安全壁垒
数据防泄密软件通过集成多种安全防护技术,旨在全面保护企业数据的安全性和保密性。以安企神软件为例,其实现全面防泄密的方式主要包括以下7个方面,为企业筑起数据安全壁垒。 1. 透明加密技术 安企神软件采用先进的透明加密技术,确…...
GNU/Linux - systemd介绍
systemd官网: System and Service Manager systemd systemd Github地址: https://github.com/systemd/systemd 首次发布 2010年3月30日 System and Service Manager systemd 是一套 Linux 系统的基本构件。它提供了一个系统和服务管理器,作为…...
如何理解递归
在二叉树的题目中,我们难免会用到递归方法,递归思想很简单,但运用起来却因为抽象而难以理解。 理解递归的关键在于认识到它是一种解决问题的方法,允许函数直接或间接地调用自身。以下是对递归的概述以及如何理解它的几个要点&…...
Spring Cache sync属性
在Spring Cache中,Cacheable注解用于标记一个方法,使其返回值可以被缓存。sync属性是Spring 4.3引入的一个新特性,用于控制缓存的同步行为。 sync 属性 sync属性的默认值是false,表示异步缓存。如果将sync设置为true,…...
【Unity】通用GM QA工具 运行时数值修改 命令行 测试工具
GM工具使用: GM工具通常用于游戏运行时修改数值(加钱/血量)、解锁关卡等,用于快速无死角测试游戏。一个通用型GM工具对于游戏项目是非常实用且必要的,但通用不能向易用妥协,纯命令行GM门槛太高,对QA不友好。 这类运行时命令行工具…...
[Spring] Spring原理(SpringBoot完结)
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...
python | rq,一个无敌的 关于Redis 的Python 库!
本文来源公众号“python”,仅用于学术分享,侵权删,干货满满。 原文链接:rq,一个无敌的 Python 库! 大家好,今天为大家分享一个无敌的 Python 库 - rq。 Github地址:https://githu…...
Redis的缓存淘汰策略
1. 查看Redis 最大的占用内存 打开redis配置文件, 设置maxmemory参数,maxmemory 是bytes字节类型, 注意转换 2. Redis默认内存多少可以用 注意: 在64bit系统下, maxmemory 设置为 0 表示不限制Redis内存使用 3. 一般生产上如何配置 一般推荐Redis 设置内…...
【C++】深度解析:用 C++ 模拟实现 priority_queue类,探索其底层实现细节(仿函数、容器适配器)
目录 ⭐前言 ✨堆 ✨容器适配器 ✨仿函数 ⭐priority_queue介绍 ⭐priority_queue参数介绍 ⭐priority_queue使用 ⭐priority_queue实现 ✨仿函数实现 ✨堆的向上调整和向下调整 ✨完整代码 ⭐前言 ✨堆 堆是一种特殊的树形数据结构,通常以二叉树的…...
1个人躲,5个人抓!《极限竞速:地平线5》全新游戏模式“捉迷藏”即将推出
风靡全球的赛车竞速游戏《极限竞速:地平线5》即将推出全新游戏模式——捉迷藏(Hide & Seek)。 《极限竞速:地平线5》日前发布了全新预告,展示了即将于 9 月 10 日推出的捉迷藏模式游戏玩法。该预告是日前举办的2024 年科隆国际游戏展 Xb…...
ARCGIS XY坐标excel转要素面
1、准备好excel 坐标 excel文件转为csv才能识别,CSV只能保留第一个工作表并且,不会保留格式。 2、在ArcGis中导入XY事件图层 创建XY事件图层 图层要素赋对象ID 将导入的图层导出为先新的图层,这样就给每个要素附加了唯一的值 选择点集转线…...
MyBatis源码系列3(解析配置文件,创建SqlSessionFactory对象)
创建SqlSessionFactory; 首先读取配置文件,使用构造者模式创建SqlSessionFactory对象。 InputStream inputStream Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder…...
企业级web应用服务器tomcat
目录 一、Web技术 1.1 HTTP协议和B/S 结构 1.2 前端三大核心技术 1.2.1 HTML 1.2.2 CSS(Cascading Style Sheets)层叠样式表 1.2.3 JavaScript 二、tomcat的功能介绍 2.1 安装 tomcat 环境准备 2.1.1 安装java环境 2.1.2 安装并启动tomcat …...
深入浅出,探讨IM(即时通讯-聊天工具)技术架构及用户界面设计
在数字化时代的浪潮中,即时通讯(IM)工具已然成为人们日常沟通的重要方式。从微信、QQ到飞信钉、喧喧IM、企业微信、钉钉、Slack,这些IM工具不仅为我们提供了便捷的沟通方式,更在技术架构和用户界面设计上展现了独特的魅…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
