【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工具不仅为我们提供了便捷的沟通方式,更在技术架构和用户界面设计上展现了独特的魅…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...
