Java多线程与线程池技术详解(八)
《星游记》 “如果只有傻瓜才相信梦想,那么就叫我大傻瓜吧!”
《一人之下》 “想走的路不好走,想做人不好做,都说是身不由己,不是废话么。己不由心,身又岂能由己!”
目录
上一篇博客习题讲解
编写一段程序,演示如何使用 ReentrantLock 实现简单的生产者-消费者模式
分析并解释为什么在某些情况下 ReentrantLock 的性能会优于 synchronized
设计一个场景,说明何时应该选择使用读写锁而不是普通的互斥锁
实现一个简单的银行账户类,包含存款、取款方法,并保证线程安全
探讨公平锁和非公平锁各自的优缺点,并给出具体的应用实例
应用实例:
第8章 结束线程与线程池任务
8.1 stop() 与 destroy()
8.2 状态值结束线程
8.3 shutdown() 与 shutdownNow()
8.4 线程休眠
8.5 线程中断
8.6 Future 与 FutureTask
8.6.1 取消任务
8.6.2 任务超时结束
8.7 项目案例:所有线程池任务暂停与重启
8.8 本章习题
上一篇博客习题讲解
Java多线程与线程池技术详解(七)
https://blog.csdn.net/speaking_me/article/details/144368977?spm=1001.2014.3001.5501
编写一段程序,演示如何使用 ReentrantLock 实现简单的生产者-消费者模式
在Java中实现生产者-消费者模式时,我们可以使用ReentrantLock
来控制对共享资源(如缓冲区)的访问。下面是一个简单的例子:
import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class ProducerConsumerExample {private final Queue<Integer> queue = new LinkedList<>();private final int LIMIT = 5; // 容器最多容纳的商品数量private final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();public void produce() throws InterruptedException {while (true) {lock.lock();try {while (queue.size() == LIMIT) {System.out.println("仓库已满,等待...");notFull.await();}int product = (int) (Math.random() * 10);queue.offer(product);System.out.println("生产者生产了:" + product);notEmpty.signalAll();} finally {lock.unlock();}Thread.sleep(1000); // 模拟生产时间}}public void consume() throws InterruptedException {while (true) {lock.lock();try {while (queue.isEmpty()) {System.out.println("仓库为空,等待...");notEmpty.await();}int product = queue.poll();System.out.println("消费者消费了:" + product);notFull.signalAll();} finally {lock.unlock();}Thread.sleep(1000); // 模拟消费时间}} }
这段代码展示了如何使用ReentrantLock
和Condition
对象来同步生产者和消费者的活动。
分析并解释为什么在某些情况下 ReentrantLock 的性能会优于 synchronized
根据实际测试,在高并发的情况下,ReentrantLock
的性能通常优于synchronized
。这是因为ReentrantLock
提供了更多的功能,例如公平锁、非阻塞锁获取尝试(如tryLock()
)、以及锁中断等特性。这些额外的功能使得开发者可以根据具体的应用场景优化线程的行为,从而提高系统的整体效率。
此外,ReentrantLock
允许程序员更精细地控制锁的获取与释放,这有助于减少不必要的上下文切换,进而提升性能。例如,可以将锁的范围缩小到最小必要的部分,避免长时间持有锁造成的瓶颈。相比之下,synchronized
关键字则总是锁定整个方法或代码块,可能会导致更高的竞争开销。
设计一个场景,说明何时应该选择使用读写锁而不是普通的互斥锁
假设我们有一个缓存系统,其中数据主要是用来查询的,而更新操作相对较少。在这种情况下,如果多个线程同时进行读取操作,则没有必要阻止它们;只有当有写入请求到来时,才需要确保独占访问以防止数据不一致的问题。因此,这里非常适合采用读写锁(ReentrantReadWriteLock
)来代替传统的互斥锁。
通过这种方式,我们可以让多个读线程并发执行,而在写入期间阻止所有其他类型的访问,这样既保证了数据的一致性,又提高了系统的吞吐量。
实现一个简单的银行账户类,包含存款、取款方法,并保证线程安全
对于银行账户类,我们可以分别用synchronized
和ReentrantLock
两种方式来保证线程安全。以下是基于ReentrantLock
的实现示例:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;class BankAccountWithReentrantLock {private double balance = 0;private final Lock lock = new ReentrantLock();private final Condition sufficientBalance = lock.newCondition();public void deposit(double amount) {lock.lock();try {balance += amount;System.out.println(Thread.currentThread().getName() + " 存款成功,余额为:" + balance);sufficientBalance.signalAll(); // 唤醒等待取款的线程} finally {lock.unlock();}}public void withdraw(double amount) throws InterruptedException {lock.lock();try {while (balance < amount) {System.out.println(Thread.currentThread().getName() + " 余额不足,等待存款...");sufficientBalance.await();}balance -= amount;System.out.println(Thread.currentThread().getName() + " 取款成功,余额为:" + balance);} finally {lock.unlock();}} }
对比之下,使用synchronized
的方式更加简洁,因为不需要显式地管理锁对象。然而,正如前面提到的,ReentrantLock
提供了更多的灵活性和更好的性能表现,尤其是在高并发环境下。
探讨公平锁和非公平锁各自的优缺点,并给出具体的应用实例
公平锁意味着每个等待中的线程按照请求锁的时间顺序依次获得锁。这种机制虽然能保证线程间的公平性,但也可能导致较低的吞吐量,因为每次都需要检查队列中的下一个线程是否应该被授予锁。而非公平锁则允许新到达的线程直接尝试获取锁,即使已经有其他线程正在等待。这样做可以显著提高吞吐量,但可能会造成饥饿现象——即某些线程长期得不到服务。
应用实例:
非公平锁:在一个高频交易系统中,为了最大化交易处理的速度,可以选择非公平锁。尽管偶尔会有线程得不到立即响应,但是大多数情况下,快速进入临界区的能力将极大促进系统的响应速度。例如,在这样的环境中,每一个微秒的延迟都可能影响到交易的结果和公司的收益。因此,使用非公平锁可以减少线程在等待队列中的时间,使得更多的交易能够在更短的时间内完成。这不仅提高了系统的吞吐量,还增强了市场的竞争力。此外,由于高频交易通常涉及大量的并发操作,非公平锁可以通过允许新到来的线程直接竞争锁来避免不必要的上下文切换,从而进一步优化性能。
公平锁:相反地,在一个任务调度系统中,如果希望确保每个任务都能及时得到处理而不至于无限期延迟,则更适合采用公平锁。即使这样会牺牲一些性能,但在这种情况下,公平性和可预测性往往更重要。例如,在批处理作业或后台任务管理系统中,任务的执行顺序可能是非常关键的。使用公平锁可以保证所有提交的任务按照它们到达的时间顺序被执行,防止任何单个任务因为其他任务频繁抢占资源而被长时间搁置。这对于那些对服务级别协议(SLA)有严格要求的应用程序尤为重要,比如金融服务中的账单处理或者电信行业的计费系统,其中任务的及时性和准确性是至关重要的。
第8章 结束线程与线程池任务
8.1 stop() 与 destroy()
在早期版本的Java中,Thread
类提供了stop()
和destroy()
方法用于停止线程。然而,这些方法已经被弃用,并且从JDK 1.2开始不再推荐使用。stop()
方法的问题在于它会突然终止线程,可能导致资源泄漏或对象处于不一致状态。例如,当一个线程持有锁时被强制停止,那么其他等待该锁的线程将永远无法获得这个锁,从而导致死锁。
至于destroy()
方法,虽然其初衷是为了立即销毁一个线程,但它从未真正实现过,因为直接销毁一个正在运行的线程可能会留下未完成的工作,如文件未关闭、网络连接未释放等。因此,在现代Java编程实践中,我们应该避免使用这两种方法来结束线程,而是采用更安全的方式,比如通过设置标志位让线程自行退出循环,或者利用线程池提供的API来进行线程管理。
8.2 状态值结束线程
为了优雅地结束线程,通常我们会定义一个布尔变量作为线程的状态标志,用来指示线程是否应该继续执行。下面是一个简单的例子,展示了如何使用状态值来控制线程的生命周期:
public class MyTask implements Runnable {private volatile boolean running = true; // 使用volatile确保可见性@Overridepublic void run() {while (running) {try {// 执行一些工作...System.out.println("Working...");Thread.sleep(1000); // 模拟工作时间} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复中断状态break;}}System.out.println("Task finished.");}public void shutdown() {running = false;}
}
在这个例子中,我们可以通过调用shutdown()
方法来通知线程停止工作,然后线程会在下一次检查状态时自然地退出循环。
8.3 shutdown() 与 shutdownNow()
ExecutorService
接口提供了两种关闭线程池的方法:shutdown()
和shutdownNow()
。前者只会停止接收新的任务,并允许已经提交的任务继续执行完毕;而后者则试图立即停止所有正在执行的任务,并返回尚未开始的任务列表。需要注意的是,即使调用了shutdownNow()
,也不能保证所有任务都会立刻停止,因为有些任务可能不会响应中断信号。
以下是关于这两个方法的一个简短示例:
import java.util.concurrent.*;public class ThreadPoolShutdownExample {public static void main(String[] args) throws InterruptedException {ExecutorService executor = Executors.newFixedThreadPool(2);// 提交几个任务for (int i = 0; i < 5; i++) {executor.submit(() -> {try {System.out.println(Thread.currentThread().getName() + " is working.");Thread.sleep(2000);} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " was interrupted.");}});}// 关闭线程池executor.shutdown(); // 或者使用 executor.shutdownNow();// 等待所有任务完成if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {System.out.println("Tasks did not terminate in time.");}} }
这段代码首先创建了一个固定大小为2的线程池,并向其中提交了五个任务。接着,根据选择调用shutdown()
或shutdownNow()
来尝试关闭线程池。最后,程序会等待最多60秒以确保所有任务都已完成。
8.4 线程休眠
线程休眠是使当前线程暂停执行一段时间的操作,这可以通过调用静态方法Thread.sleep(long millis)
实现,其中millis
是以毫秒为单位的时间长度。在此期间,线程不会占用CPU资源,也不会参与任何计算活动。一旦指定的时间过去,线程就会自动恢复执行。需要注意的是,如果在线程休眠期间发生中断异常(InterruptedException
),我们应该适当地处理这个异常,通常是重新设置线程的中断状态。
try {Thread.sleep(1000); // 让当前线程休眠1秒 } catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复中断状态 }
8.5 线程中断
线程中断是一种协作机制,允许一个线程请求另一个线程停止其所做的工作。要中断一个线程,可以调用它的interrupt()
方法。被中断的线程可以在适当的时候检查自己的中断状态,并采取相应的行动。例如,如果线程正在调用sleep()
、wait()
或其他支持中断的方法,那么它会在检测到中断后抛出InterruptedException
。此外,还可以通过调用Thread.interrupted()
或isInterrupted()
来手动查询线程的中断状态。
// 中断线程 thread.interrupt();// 在目标线程内部定期检查中断状态 if (Thread.currentThread().isInterrupted()) {// 清理资源并准备退出 }
8.6 Future 与 FutureTask
Future
接口表示异步计算的结果,提供了获取结果、取消任务以及查询任务状态的方法。FutureTask
是一个实现了Runnable
和Future
接口的具体类,它可以包装一个Callable
对象,并允许将其提交给线程池执行。当任务完成后,可以通过调用get()
方法来检索结果;如果任务还没有完成,则此方法会阻塞直到结果可用。此外,FutureTask
还支持取消操作,即通过调用cancel(boolean mayInterruptIfRunning)
来尝试取消任务。
import java.util.concurrent.*;public class FutureTaskExample {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<Integer> task = () -> {// 模拟耗时操作Thread.sleep(5000);return 42;};FutureTask<Integer> futureTask = new FutureTask<>(task);Thread thread = new Thread(futureTask);thread.start();// 获取结果,如果任务还未完成,这里会阻塞Integer result = futureTask.get();System.out.println("Result: " + result);} }
对于取消任务,如果我们希望尽可能快地停止任务,可以传递参数true
给cancel()
方法,这样不仅会标记任务为已取消,还会尝试中断正在执行的任务。但是请注意,并不是所有的任务都能响应中断,特别是那些长时间运行而不检查中断状态的任务。
8.6.1 取消任务
如前所述,Future
接口提供了一个cancel(boolean mayInterruptIfRunning)
方法,用于尝试取消正在进行的任务。如果任务已经开始执行并且mayInterruptIfRunning
设为true
,那么系统会尝试中断任务。不过,正如前面提到的,实际效果取决于任务本身是否正确处理了中断信号。如果任务没有及时响应中断,那么它可能仍然会继续执行一段时间。
8.6.2 任务超时结束
有时候我们需要限制任务的最大执行时间,以防止它们无限期地挂起。为此,Future
接口提供了带有时限的get(long timeout, TimeUnit unit)
方法。如果任务未能在规定时间内完成,将会抛出TimeoutException
。此时,我们可以选择放弃任务或将它重新排队稍后再试。
try {Integer result = futureTask.get(3, TimeUnit.SECONDS);System.out.println("Result: " + result); } catch (TimeoutException e) {System.out.println("Task timed out.");futureTask.cancel(true); // 尝试取消任务 }
8.7 项目案例:所有线程池任务暂停与重启
假设我们有一个场景,需要能够在特定条件下暂停所有线程池中的任务,并在条件满足时恢复它们。虽然Java的标准库并没有直接提供这样的功能,但我们可以通过自定义的方式来实现这一点。一种常见的做法是在每个任务内部添加逻辑来监听外部信号,例如使用CountDownLatch
或CyclicBarrier
等同步辅助工具。另一种方法是利用Semaphore
来控制并发度,从而间接达到暂停的效果。
下面是一个基于Semaphore
的例子,演示了如何暂停和恢复线程池中的任务:
import java.util.concurrent.*;public class PausingThreadPool {private final ExecutorService executor;private final Semaphore semaphore;public PausingThreadPool(int poolSize) {this.executor = Executors.newFixedThreadPool(poolSize);this.semaphore = new Semaphore(poolSize);}public void submit(Runnable command) {try {semaphore.acquire(); // 获取许可} catch (InterruptedException e) {Thread.currentThread().interrupt();return;}executor.submit(() -> {try {command.run();} finally {semaphore.release(); // 释放许可}});}public void pause() {semaphore.drainPermits(); // 清空所有许可}public void resume() {int permits = executor.getPoolSize();semaphore.release(permits); // 恢复所有许可}public void shutdown() {executor.shutdown();}public static void main(String[] args) throws InterruptedException {PausingThreadPool pool = new PausingThreadPool(2);// 提交任务for (int i = 0; i < 5; i++) {pool.submit(() -> {try {System.out.println(Thread.currentThread().getName() + " is working.");Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 暂停任务pool.pause();System.out.println("Tasks paused.");// 等待几秒钟后恢复Thread.sleep(5000);pool.resume();System.out.println("Tasks resumed.");// 最终关闭线程池pool.shutdown();} }
在这个例子中,我们创建了一个名为PausingThreadPool
的类,它封装了一个普通的线程池,并通过Semaphore
来控制任务的并发执行。当我们想要暂停任务时,只需调用pause()
方法清空所有许可;当准备好恢复时,则调用resume()
方法一次性释放足够的许可让所有等待的任务继续执行。这种方法有效地实现了任务的暂停与恢复功能,同时保持了原有线程池的行为不变。
8.8 本章习题
为了帮助您更好地理解和掌握本章内容,请尝试解答以下问题:
- 编写一段程序,展示如何使用
ReentrantLock
实现生产者-消费者模式,并解释为什么在某些情况下ReentrantLock
的表现优于synchronized
。 - 设计一个场景,说明何时应该选择使用读写锁而不是普通的互斥锁,并给出具体的理由。
- 实现一个简单的银行账户类,包含存款、取款方法,并确保线程安全。分别使用
synchronized
和ReentrantLock
来解决同步问题,比较两者的异同点。 - 探讨公平锁和非公平锁各自的优缺点,并给出具体的应用实例。
- 分析并解释为什么
shutdown()
和shutdownNow()
之间存在差异,以及它们各自适用的情况是什么? - 讨论
Future
和FutureTask
的作用,包括如何取消任务和处理任务超时的情况。 - 创建一个小项目,模拟一个可以暂停和恢复所有线程池任务的系统,考虑如何实现这一功能以及可能遇到的问题。
相关文章:
Java多线程与线程池技术详解(八)
《星游记》 “如果只有傻瓜才相信梦想,那么就叫我大傻瓜吧!” 《一人之下》 “想走的路不好走,想做人不好做,都说是身不由己,不是废话么。己不由心,身又岂能由己!” 目录 上一篇博客习题讲解 编…...
2024年12月11日Github流行趋势
项目名称:maigret 项目维护者:soxoj, kustermariocoding, dependabot, fen0s, cyb3rk0tik项目介绍:通过用户名从数千个站点收集个人档案信息的工具。项目star数:12,055项目fork数:870 项目名称:uv 项目维护…...
ThinkPHP 6.0 PHP新手教程
1、系统配置文件 下面系统自带的配置文件列表及其作用: 配置文件名描述app.php应用配置cache.php缓存配置console.php控制台配置cookie.phpCookie配置database.php数据库配置filesystem.php磁盘配置lang.php多语言配置log.php日志配置middleware.php中间件配置rou…...
【Excel学习记录】02-单元格格式设置
1.单元格格式工具美化表格 单元格格式位置 选中单元格,右键→设置单元格格式 合并居中 跨越合并 字体类型、大小、颜色、填充底纹、边框 斜线 软回车:alt enter 格式刷 2.单元格数字格式 格式不影响数值,只是展示形式 日期本质也是数…...
Paimon Tag和Branch创建文件存储过程
结论: 如果data-file被引用则不会被压缩,压缩仅针对未被引用的文件,创建tag时候根据当前快照进行创建 1、实际表和Manifest的内容 查看tag的内容 select * from table$tags;或者直接查看tag ossutil cat oss://test-dataware/warehouse/te…...
HCIA笔记8--DHCP、Telnet协议
1. DHCP介绍 对于主机的网络进行手动配置,在小规模的网络中还是可以运作的,但大规模网络是无力应对的。因此就有了DHCP协议来自动管理主机网络的配置。 DHCP(Dynamic Host Configuration Protocol): 动态主机配置协议,主要需要配置的参数有…...
Tableau数据可视化与仪表盘搭建
1.Tableau介绍 可视化功能 数据赋能 数据赋能就是将我们的数据看板发布到我们的线上去 这里的IP地址是业务部门可以通过账号密码登入的 我们也可以根据需要下载,选中并点击下载即可 下载下来之后,自己就能根据数据进行自定义的分析 也可以下载图片 还有…...
Django结合websocket实现分组的多人聊天
其他地方和上一篇大致相同,上一篇地址点击进入, 改动点1:在setting.py中最后再添加如下配置: # 多人聊天 CHANNEL_LAYERS {"default":{"BACKEND": "channels.layers.InMemoryChannelLayer"} }因此完整的se…...
自动驾驶控制与规划——Project 1: 车辆纵向控制
目录 零、任务介绍一、环境配置1.1 CARLA的配置1.2 Docker Ubuntu 20.04 ROS2 Foxy的配置 二、算法2.1 定速巡航2.2 自适应巡航2.3 离散PID控制 三、代码实现3.1 代码补全3.2仿真验证 零、任务介绍 课程主页 配置Carla仿真器配置carla-ros-bridge补全src\ros-bridge\carla_s…...
基于python实现自动化的验证码识别:探索与实践
基于python实现自动化的验证码识别:探索与实践 一、验证码的类型及特点(一)图像验证码(二)短信验证码(三)语音验证码 二、验证码识别的方法*(一)传统图像处理方法&#x…...
如何保证消息队列的高可用?(RabbitMQ)
RabbitMQ 是基于主从(非分布式)做高可用性的,RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式 1、单机模式:一般没人生产用单机模式 2、普通集群模式: 普通集群模式用于提高系统的吞吐量&…...
es的join是什么数据类型
在 Elasticsearch 中,parent 并不是一个独立的数据类型,而是与 join 数据类型一起使用的一个概念。join 数据类型用于在同一个索引中建立父子文档之间的关系,允许你在一个索引内表示层级结构或关联关系。通过 join 字段,你可以定义不同类型的文档(如父文档和子文档),并指…...
爬虫基础之代理的基本原理
在做爬虫的过程中经常会遇到一种情况,就是爬虫最初是正常运行、正常抓取数据的,一切看起来都是那么美好,然而一杯茶的工夫就出现了错误,例如 403 Forbidden,这时打开网页一看,可能会看到“您的IP访问频率太…...
golang实现简单的reids服务2
golang实现redis兼容的redis服务实现redis兼容的redis服务思路 golang实现redis兼容的redis服务 之前做的redis服务是通过tcp封装的自定义协议 原版项目地址:https://github.com/dengjiayue/my-redis.git 那么能不能实现一个redis兼容的redis服务,这样一般的redis包也可以调…...
CSS常用的尺寸单位
像素px 以屏幕上的一个点为单位,比较稳定和精确用的最多 em 以字体大小为参考,(是自身字体大小的倍数)当自身的字体大小改变时,em也会随着改变em * font-size px rem 以根元素 < html > 作为参考ÿ…...
计算机网络 备查
OSI 七层模型 七层模型协议各层实现的功能 简要 详细 TCP/IP协议 组成 1.传输层协议 TCP 2.网络层协议 IP 协议数据单元(PDU)和 封装 数据收发过程 数据发送过程 1. 2.终端用户生成数据 3.数据被分段,并加上TCP头 4.网络层添加IP地址信息…...
JavaEE 【知识改变命运】05 多线程(4)
文章目录 单例模式什么是单例模式饿汉模式懒汉模式多线程- 懒汉模式分析多线程问题第一种添加sychronized的方式第二种添加sychronized的方式改进第二种添加sychronized的方式(DCL检查锁) 阻塞队列什么是阻塞队列什么是消费生产者模型标准库中的阻塞队列…...
迭代器和生成器
一、迭代器(Iterator) 1. 什么是迭代器? 迭代器是一个可以在某一集合(如列表、元组等)中逐个访问元素的对象。它提供了一个方法,可以记住遍历的位置,每次取出一个元素,直到所有元素…...
小发现,如何高级的顺序输出,逆序输出整数的每一位(栈,队列)
当我还是初学者的时候,我经常思考有没有比慢慢求每一位数字然后考虑正序,逆序输出要快的办法...长期琢磨,必有所获! 我刚学数据结构的时候还没意识到栈,队列还能这样用,虽然说有点杀鸡用牛刀的感觉&#x…...
前端换行、空格的多种表现形式
换行 1、<br> 标签 这是最直接的方式,用于在文本中插入一个简单的换行。<br> 标签是一个空元素,意味着它不需要结束标签。 示例: <p>这是第一行。<br>这是第二行。</p>2、CSS white-space 属性 通过CSS的w…...
自己总结:selenium高阶知识
全篇大概10000字(含代码),建议阅读时间30min 一、等待机制 如果有一些内容是通过Ajax加载的内容,那就需要等待内容加载完毕才能进行下一步操作。 为了避免人为操作等待,会遇到的问题, selenium将等待转换…...
无线遥控红外通信
无线遥控红外通信 红外发射装置一般是指红外遥控器由 键盘电路 ,红外编码电路 电源电路 和红外发射 电路组成 一般的红外线波长为940nm左右,外形与普通发光二极管相同 红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送…...
第一个C++程序--(蓝桥杯备考版)
第一个C程序 基础程序 #include <iostream>//头⽂件 using namespace std;//使⽤std的名字空间 int main()//main函数 {cout << "hello world!" << endl; //输出:在屏幕打印"hello world!" return 0;}main函数 main 函数是…...
Rust包管理和构建工具
Cargo 是 Rust 语言的包管理和构建工具。它提供了一套完整的工具链,用于管理 Rust 项目的依赖关系、编译代码、运行测试和生成文档。Cargo 极大地简化了 Rust 项目的开发和部署过程,使得开发者可以专注于编写代码,而不是处理构建系统的复杂性…...
STM32输入捕获详解
目录 一、引言 二、输入捕获原理 三、寄存器介绍 四、配置步骤 1.开启时钟 2.GPIO 初始化 3.初始化定时器 4.配置输入捕获模式 5.使能捕获和更新中断 6.设置中断分组并编写中断服务函数 7.使能定时器 五、程序示例 六、总结 一、引言 在嵌入式系统开发中࿰…...
利用高德地图API,如何在PHP与vue3中实现地图缩放功能
文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…...
Selenium WebDriver:自动化网页交互的利器
Selenium WebDriver:自动化网页交互的利器 在当今快速发展的Web开发领域,自动化测试已经成为确保应用程序质量和用户体验的重要手段。Selenium WebDriver,作为Selenium工具包中的核心组件,正是这一领域的佼佼者。本文将详细介绍S…...
uniapp -- 实现页面滚动触底加载数据
效果 首选,是在pages.json配置开启下拉刷新 {"path": "pages/my/document/officialDocument","style": {"navigationStyle":</...
用ChatGPT-o1进行论文内容润色效果怎么样?
目录 1.引导问题发现 2.角色设定 3.整理常问修改 4.提供样例 5.小细节 小编在这篇文章中分享如何充分利用ChatGPT-o1-preview来提升论文润色的技巧。小编将持续跟进最新资源和最新的调研尝试结果,为宝子们补充更多实用的写作技巧。这些技巧将有助于您更有效地利…...
6 C/C++输⼊输出(下)(未完续)
1. OJ(online judge)题⽬输⼊情况汇总 在竞赛的 OJ 题⽬中,⼀般关于输⼊场景总结为下⾯四类: 接下来,我们就结合题⽬,给⼤家分别介绍。 1.1 单组测试⽤例 练习1 B2009 计算 (ab)/c 的值 - 洛谷 | 计算机科…...
题海拾贝:力扣 20、有效的括号
Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路! 我的博客:<但凡.-CSDN博客 我的专栏:《编程之路》、《题海拾贝》、《数据结构与算法之美》 欢迎点赞、关注! 1、题目 2、题解 这…...
视频推拉流EasyDSS无人机直播技术巡查焚烧、烟火情况
焚烧作为一种常见的废弃物处理方式,往往会对环境造成严重污染。因此,减少焚烧、推广绿色能源和循环经济成为重要措施。通过加强森林防灭火队伍能力建设与长效机制建立,各地努力减少因焚烧引发的森林火灾,保护生态环境。 巡察烟火…...
基于Hermite多项式的三维反时间波的生成
原创:daode3056(daode1212) 反时间波,也称为时间反演波,是一种在特定条件下能够实现波的聚焦和传播的技术。反时间波的产生基于时间反演技术,其原理和方法通常有: 1. [时间反演信号处理原理]: 时间反演技术并不是指时间…...
数据结构与算法复习AVL树插入过程
环境 $ cat /proc/version Linux version 6.8.0-45-generic (builddlcy02-amd64-115) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.2.0-23ubuntu4) 13.2.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #45-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 30 12:02:04 UTC 2024 #include <std…...
MetaGPT源码 (Memory 类)
目录 MetaGPT源码:Memory 类例子 MetaGPT源码:Memory 类 这段代码定义了一个名为 Memory 的类,用于存储和管理消息(Message)对象。Memory 提供了多种操作消息的功能,包括添加单条或批量消息、按角色或内容筛选消息、删除最新消息…...
day1数据结构,关键字,内存空间存储与动态分区,释放
小练习 在堆区空间连续申请5个int类型大小空间,用来存放从终端输入的5个学生成绩,然后显示5个学生成绩,再将学生成绩升序排序,排序后,再次显示学生成绩。显示和排序分别用函数完成(两种排序方法࿰…...
C# 用封装dll 调用c++ dll 使用winapi
这里用c net 封装winapi函数 pch.h // pch.h: 这是预编译标头文件。 // 下方列出的文件仅编译一次,提高了将来生成的生成性能。 // 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 // 但是,如果此处列出的文件中的任何一个…...
vue2 如何设置i18n的默认语言为当前浏览器的语言
做到i18n这里设置默认语言的时候遇到了一些小问题,所以做个记录: 原始代码lang/index // index.js import Vue from vue import VueI18n from vue-i18n import Cookies from js-cookie // import elementEnLocale from element-ui/lib/locale/lang/en // element-…...
QT数据库SQLite:QsqlTableModel使用总结
数据库连接、数据模型与界面组件所涉及的类之间的关系如下所示: 数据库类 QSqlDatabase 类用于建立与数据库的连接,QSqlDatabase 对象就表示这种连接。QSqlDatabase 类的功能主要分为三大部分: 1、创建数据库连接,即创建 QSqlDat…...
服务器零配件
阵列卡 H3C电池 RAId 卡 内存条位置 HBA卡 MOC卡...
MySQL 学习 之 批量插入数据性能问题
文章目录 现象优化 现象 在使用 kettle 同步大数据的数据到我们的 MySQL 数据库中时发现,数据量大时插入效率很慢,大约在 2000/s 优化 在 MySQL 驱动连接中添加 rewriteBatchedStatementstrue 参数,减少 网络 IO DB IO 耗时 默认关闭指定…...
会话管理和身份验证和授权
Cookie、Session、Token Cookie 简介:[Cookie]是一种小型文本文件,由服务器发送到用户的浏览器并保存在用户的计算机上。其主要作用是识别用户身份、跟踪用户活动、保存用户设置等。Cookie通常由名称、值、域名、路径、过期时间等字段组成,并…...
RK3588 rknpu2/rkllm/rockit/mpp/rga 等源码验证
RK3588 简介 本项目基于rk3588硬件平台,将嵌入式、流媒体、AI等相关的技术验证源码地址 源码说明 buildroot 为buildroot使用方法dk_doc 为rk的文档mpp 在mpp例子上增加推流rga 为rk3588的硬件加速模块,可快速处理视频,提供的API接口与op…...
【CSS in Depth 2 精译_075】12.2 Web 字体简介 + 12.3 谷歌字体的用法
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第四部分 视觉增强技术 ✔️【第 12 章 CSS 排版与间距】 ✔️ 12.1 间距设置 12.1.1 使用 em 还是 px12.1.2 对行高的深入思考12.1.3 行内元素的间距设置 12.2 Web 字体 ✔️12.3 谷歌字体 ✔️12.…...
【数字花园】个人知识库网站搭建:①netlify免费搭建数字花园
目录 [[数字花园]]的构建原理包括三个步骤:五个部署方案教程相关教程使用的平台 步骤信息管理 这里记录的自己搭建数字花园(在线个人知识库)的经历,首先尝试的是网上普遍使用的方法,也就是本篇文章介绍的。 后面会继续…...
访问者模式的理解和实践
在软件开发过程中,设计模式为我们提供了解决常见问题的最佳实践。访问者模式(Visitor Pattern)是行为设计模式之一,它将数据操作与数据结构分离,使得在不修改数据结构的前提下,能够定义作用于这些元素的新的…...
SpringBoot中Selenium详解
文章目录 SpringBoot中Selenium详解一、引言二、集成Selenium1、环境准备1.1、添加依赖 2、编写测试代码2.1、测试主类2.2、页面对象2.3、搜索组件 三、使用示例四、总结 SpringBoot中Selenium详解 一、引言 在现代软件开发中,自动化测试是提高软件质量、减少重复…...
Android 系统应用重名install安装失败分析解决
Android 系统应用重名install安装失败分析解决 文章目录 Android 系统应用重名install安装失败分析解决一、前言1、Android Persistent apps 简单介绍 二、系统 persistent 应用直接安装需求分析解决1、系统应用安装报错返回的信息2、分析解决 三、其他1、persistent系统应用in…...
scala中如何解决乘机排名相关的问题
任务目标: 1.计算每个同学的总分和平均分 2.按总分排名,取前三名 3.按单科排名,取前三名 好的,我们可以用Scala来完成这个任务。下面是一个简单的示例代码,它将演示如何实现这些功能: // 假设我们有一个…...
常用的注解
RequestMapping 用于映射请求路径 可以添加在类或方法上 请求类型 请求类型包括GET、POST、PUT、DELETE等 默认支持GET和POST两种方式 简写:GetMapping、PostMapping、PutMapping、DeleteMapping PostMapping("/buy") 等价 RequestMapping("/buy&quo…...