Java并发—ReetrantLock详解及应用

目录

一、ReetrantLock的特性

1、非阻塞获取锁

2、带超时的锁获取:

3、锁的公平性

4、锁的可中断性

5、Condition条件变量

6、锁的可重入性

可重入锁

不可重入锁

7、性能优化

二、ReentrantLock和Synchronized的区别

1、语法和使用方式

2、锁的获取和释放

3、高级特性

4、条件变量

5、性能

总结

三、ReentrantLock使用场景


之前的文章Java并发—synchronized关键字的应用-CSDN博客讲述了sychronized的应用,那为什么还需要其他的锁呢?

在使用Synchronized,会存在以下几个问题:

  • 不可中断锁,需要线程执行完才会释放锁(synchronized的获取和释放锁由jvm实现)

  • 非公平锁

  • Synchronized引入了偏向锁,轻量级锁(自旋锁)后,性能有所提升

synchronized属于隐式锁,即锁的持有与释放都是隐式的,可能会导致死锁

为了可以灵活地控制锁,就需要使用到显式锁,即锁的持有和释放都必须手动编写

ReentrantLock是一把可重入锁互斥锁,它具有与 Synchronized 关键字相同的含有隐式监视器锁(monitor)的基本行为和语义,但是它比 Synchronized 具有更多的方法和功能

在Java 1.5中,官方在concurrent并发包中加入了Lock接口,ReentrantLock位于java.util.concurrent.locks包下,实现了Lock接口和Serializable接口,该接口中提供了lock()方法和unLock()方法对显式加锁和显式释放锁操作进行支持

 public class ReentrantLock implements Lock, java.io.Serializable {……}

Lock lock = new ReentrantLock();public void save(){try{lock.lock();//业务代码……}finally{lock.unlock();}
}

从上述代码可以使用ReentrantLock来管理锁,确保在save方法执行期间对资源的独占访问。通过try-finally结构确保即使发生异常也能正确地使用lock.unlock()释放锁 

ReentrantLock实现了Lock接口,Lock接口是Java中对锁操作行为的统一规范

ReentrantLock结构:

🌰:多线程使用ReentrantLock获取资源

public class ReentrantLockTest {private static final Lock lock = new ReentrantLock();​​public static void test() {try {//获取锁lock.lock();System.out.println(Thread.currentThread().getName() + "获取到锁了");//业务代码,使用部分花费100毫秒Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();} finally {//释放锁放在finally中。lock.unlock();System.out.println(Thread.currentThread().getName() + "释放了锁");}}​public static void main(String[] args) {new Thread(() -> { test(); }, "线程1").start();new Thread(() -> { test(); }, "线程2").start();}}​

 运行结果:

效果和Synchronized的一样,线程1获取到锁了,线程2需要等待线程1释放锁后才可以获取锁

⚠️注意:为了防止锁不被释放,从而造成死锁,强烈建议把锁的释放lock.unlock()放在finally模块中

一、ReetrantLock的特性

不仅如此,ReetrantLock相对于synchronized解决了很多问题:

上述代码lock.lock();会阻塞当前线程直至获取到锁为止,那么为了避免这个问题就需要使用lock.tryLock()

1、非阻塞获取锁

ReentrantLock提供了tryLock()方法,可以尝试获取锁而不阻塞当前线程。

  • 如果锁当前未被任何线程持有,则tryLock()方法会立即获取锁并返回true。
  • 如果锁当前已被其他线程持有,则tryLock()方法不会阻塞当前线程,而是立即返回false。

代码如下:

     import java.util.concurrent.locks.ReentrantLock;public class DemoService {//将ReentrantLock实例声明为final可以确保锁对象的不变性,提高线程安全性private final ReentrantLock lock = new ReentrantLock();public void save() {try {lock.lock();// 业务代码……} finally {lock.unlock();}}}

2、带超时的锁获取:

ReentrantLock提供了tryLock(long timeout, TimeUnit unit)方法,允许线程在指定时间内尝试获取锁

如果在指定时间内未能获取到锁,则线程不会被阻塞,而是返回false

🌰:多线程获取超时锁的案例

     public class TryLockWithTimeoutExample {private final ReentrantLock lock = new ReentrantLock();public void save() {if (!lock.tryLock(5, TimeUnit.SECONDS)) {// 如果未能在5秒内获取锁,可以记录日志或采取其他措施return; // 或者抛出异常}try {// 业务代码……} catch (Exception e) {// 处理异常throw e;} finally {lock.unlock();}}public static void main(String[] args) {TryLockWithTimeoutExample example = new TryLockWithTimeoutExample();new Thread(example::save).start();new Thread(example::save).start();}}

3、锁的公平性

ReentrantLock允许创建公平锁非公平锁

  • 公平锁按照线程请求锁的顺序来分配锁,可以减少线程之间的饥饿现象。保证等待时间最长的线程优先获取锁,其实就是先入队的先得锁,即FIFO
  • 非公平锁不保证请求锁的顺序,可能会让后来的线程优先获取锁

公平锁常见的场景:多线程任务顺序处理、线程池

默认情况下ReentrantLock使用非公平锁

/*** 默认创建非公平锁*/
public ReentrantLock() {sync = new NonfairSync();
}/*** fair为true表示是公平锁,fair为false表示是非公平锁*/
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

🌰:多线程获取公平锁的案例

public class FairReentrantLockExample {private final ReentrantLock lock = new ReentrantLock(true); // true 表示公平锁public void processTask(int taskId) {lock.lock();try {System.out.println("Processing task " + taskId + " by " + Thread.currentThread().getName());// 业务代码……} finally {lock.unlock();}}public static void main(String[] args) {FairReentrantLockExample example = new FairReentrantLockExample();for (int i = 1; i <= 5; i++) {int taskId = i;new Thread(() -> example.processTask(taskId)).start();}}
}

执行顺序:

  • 创建线程:主线程创建5个新线程,每个线程都会调用 processTask(),线程被创建后,它们就会被加入到线程调度器的就绪队列中,等待被调度执行
  • 请求锁
    • 每个线程开始执行时,都会尝试获取锁
    • 由于使用的是公平锁,线程将按照它们请求锁的顺序来获取锁
  • 执行过程
    • 线程1首先调度执行,如果获取到锁开始执行processTask()
    • 其他线程将被阻塞,直至锁可用的状态
    • 线程1执行完执行processTask(),释放锁
    • 线程2请求锁,获取锁开始执行processTask(),此过程将重复,直至所有线程都执行完毕

效果:

Processing task 1 by Thread-0
Processing task 2 by Thread-1
Processing task 3 by Thread-2
Processing task 4 by Thread-3
Processing task 5 by Thread-4

例如:可以使线程池中的线程公平地获取锁,以确保线程按照一定的顺序获取锁,从而避免某些线程长时间无法获取锁导致的饥饿问题

🌰:线程池使用公平锁

     public class FairReentrantLockThreadPoolExample {private final ReentrantLock lock = new ReentrantLock(true); // true 表示公平锁private final ExecutorService executor = Executors.newFixedThreadPool(3);public void processTask(int taskId) {lock.lock();try {System.out.println("Processing task " + taskId + " by " + Thread.currentThread().getName());// 业务代码……} finally {lock.unlock();}}public static void main(String[] args) {FairReentrantLockThreadPoolExample example = new FairReentrantLockThreadPoolExample();for (int i = 1; i <= 5; i++) {int taskId = i;executor.execute(() -> example.processTask(taskId));}// 关闭线程池executor.shutdown();}}

说明:在这个例子中,我们创建了一个固定大小的线程池,其中包含3个线程

线程池将处理5个任务,每个任务都需要获取公平锁。由于线程池的大小为3,最多只有3个线程可以同时执行

当一个线程获取锁后,其他线程将被阻塞,直到锁被释放。由于使用了公平锁,线程将按照它们请求锁的顺序来获取锁

4、锁的可中断性

使用ReentrantLock时,线程可以通过中断机制来取消等待锁的操作,避免线程阻塞

lock.lockInterruptibly()获取一个可以被中断的重入锁,允许线程在等待锁的过程中响应中断信号,使用thread.interrupt()可以打断线程

🌰:多线程执行业务时被中断

     public class InterruptibleLockExample {private final ReentrantLock lock = new ReentrantLock();private final Condition condition = lock.newCondition();public void processTask()  {try {lock.lockInterruptibly();System.out.println("Processing task by " + Thread.currentThread().getName());// 业务代码……// 假设需要等待一段时间condition.await(5, TimeUnit.SECONDS);System.out.println("Task completed by " + Thread.currentThread().getName());} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " interrupted, task cancelled");} finally {lock.unlock();System.out.println(Thread.currentThread().getName() + " released the lock");}}public static void main(String[] args) throws InterruptedException {InterruptibleLockExample example = new InterruptibleLockExample();Thread thread = new Thread(example::processTask);thread.start();// 假设我们需要在一段时间后中断线程TimeUnit.SECONDS.sleep(2);thread.interrupt();}}

线程调用 condition.await(5, TimeUnit.SECONDS) 开始等待 5 秒。在等待过程中,主线程在 2 秒后中断了 Thread-0。Thread-0 在等待过程中被中断,因此将抛出 Interrup如果线程被中断,将抛出 InterruptedException,线程中断退出

效果:

Processing task by Thread-0
Thread-0 interrupted, task cancelled
Thread-0 released the lock

5、Condition条件变量

ReentrantLock支持条件变量,允许线程等待特定条件满足后再继续执行

Condition 接口是 Java 1.5 中引入的 java.util.concurrent 包的一部分,旨在提供更高级别的并发控制,设计目的是为了提供比 wait 和 notify 更加灵活和强大的线程同步机制

Condition 接口允许更细粒度的控制,比如可以有多个条件变量,每个条件变量可以独立使用,从而支持更复杂的同步模式

提供了 awaitsignalsignalAll 等方法。

  • await 方法释放锁并等待,直到被 signal 或 signalAll 方法唤醒。
  • signal 方法唤醒一个等待线程,而 signalAll 方法唤醒所有等待线程。
  • Condition 对象可以与多个锁关联,因此可以实现多个条件变量。

Condition接口可以与ReentrantLock一起使用,提供了更灵活的线程同步机制。

private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();

在上述线程中断的案例中,也使用了Condition的await()

// 假设需要等待5s时间
condition.await(5, TimeUnit.SECONDS);

通过Condition接口定义的方法我们发现跟之前Objectwaitnotify功能几乎差不多,所以使用Condition对象的方法也可以完成线程间的通信

waitnotify 方法:

  • 必须在一个已经同步的对象上调用,即必须在 synchronized 块或方法中使用。
  • wait 方法释放锁并等待,直到被 notify 或 notifyAll 方法唤醒。
  • notify 方法随机唤醒一个等待线程,而 notifyAll 方法唤醒所有等待线程。 只能使用一个同步对象来控制多个线程之间的同步

6、锁的可重入性

锁的可重入性和不可重入性主要描述了一个线程在获取锁之后是否能够再次获取同一把锁而不引起死锁的能力

可重入锁

可重入锁允许一个已经获取了锁的线程再次获取同一把锁,而不会导致其他等待该锁的线程被阻塞

因为可重入锁内部维护了一个计数器,每当同一个线程再次获取锁时,计数器加一;当线程释放锁时,计数器减一,直到计数器为零时,锁才真正被释放

ReentrantLock支持可重入性,即允许已经持有锁的线程再次获取锁。

这种特性在synchronized中也是支持的,但在ReentrantLock中更为明显和可控

🌰:假设一个银行账户需要有存款和取款的操作

先使用synchronized实现重入锁

public class Account {private int balance = 0;public synchronized void deposit(int amount) {balance += amount;// 假设我们想在存款后打印余额printBalance();}public synchronized void withdraw(int amount) {if (amount <= balance) {balance -= amount;}// 假设我们想在取款后打印余额printBalance();}public synchronized void printBalance() {System.out.println("当前余额: " + balance);}
}

在这个例子中,deposit 和 withdraw 方法都是同步的,这意味着它们只能由一个线程执行。同样,printBalance 方法也是同步的,这样当一个线程正在执行 deposit 或 withdraw 方法时,它可以安全地调用 printBalance 方法而不会导致死锁

使用ReentrantLock实现重入锁

public class Account {private int balance = 0;private final ReentrantLock lock = new ReentrantLock();public void deposit(int amount) {lock.lock();try {balance += amount;// 假设我们想在存款后打印余额printBalance();} finally {lock.unlock();}}public void withdraw(int amount) {lock.lock();try {if (amount <= balance) {balance -= amount;}// 假设我们想在取款后打印余额printBalance();} finally {lock.unlock();}}public void printBalance() {lock.lock();try {System.out.println("当前余额: " + balance);} finally {lock.unlock();}}
}

假设初始余额为 0,并且线程 T1 先执行 deposit(100),接着线程 T2 执行 withdraw(50),最后线程 T3 执行 printBalance(),那么输出可能是:

当前余额: 100
当前余额: 50

不可重入锁

不可重入锁不允许一个已经获取了锁的线程再次获取同一把锁,除非它首先释放了锁。如果一个线程试图再次获取锁,它将会被阻塞,直到锁被释放。这种类型的锁通常用于那些不需要支持递归调用的场景

由于 Java 标准库中没有直接提供的不可重入锁实现,我们可以使用 java.util.concurrent.locks.Lock 接口的实现类来模拟一个不可重入锁的行为。这里我们使用 ReentrantLock 并手动管理锁的可重入性

import java.util.concurrent.locks.ReentrantLock;public class NonReentrantAccount {private int balance = 0;private ReentrantLock lock = new ReentrantLock();public void deposit(int amount) {lock.lock();try {balance += amount;// 假设我们想在存款后打印余额printBalance();} finally {lock.unlock();}}public void withdraw(int amount) {lock.lock();try {if (amount <= balance) {balance -= amount;}// 假设我们想在取款后打印余额printBalance();} finally {lock.unlock();}}public void printBalance() {// 模拟不可重入锁行为,检查当前线程是否持有锁if (!lock.isHeldByCurrentThread()) {lock.lock();try {System.out.println("当前余额: " + balance);} finally {lock.unlock();}} else {// 如果当前线程已经持有锁,则不打印余额// 这里我们简单地跳过打印操作}}
}

使用 ReentrantLock 来实现锁的功能,但是在 printBalance 方法中,我们检查当前线程是否已经持有锁。如果是,则不执行打印操作,以此来模拟不可重入锁的行为

假设初始余额为 0,并且线程 T1 先执行 deposit(100),接着线程 T2 执行 withdraw(50),最后线程 T3 执行 printBalance(),那么输出可能是:

当前余额: 50

这是因为:

  • 线程 T1 执行 deposit(100),此时T1持有锁,因此 printBalance 不会被执行
  • 线程 T2 执行 withdraw(50),T1释放锁,T2持有锁,因此 printBalance 不会被执行
  • 线程 T3 执行 printBalance(),此时T3不持有锁,因此会先获取锁并打印当前余额 50

⚠️注意:实际应用中很少会使用这样的不可重入锁实现,因为这通常会导致代码难以理解和维护。通常情况下,更倾向于使用可重入锁来避免死锁问题

7、性能优化

ReentrantLock使用了AbstractQueuedSynchronizer(AQS)框架,可以利用现代处理器的特性(如CAS操作)来优化锁的性能

二、ReentrantLock和Synchronized的区别

通过ReentrantLock 上述的特性,就可以了解与synchronized区别了

最好是理解记忆,切记死记硬背!

1、语法和使用方式

  • synchronized:关键字,可直接作用于方法或代码块,不需要显式地调用获取和释放锁的方法
  • ReentrantLock:类,需要通过 lock() 和 unlock() 明确地获取和释放锁。

2、锁的获取和释放

  • synchronized:自动释放锁,当线程退出作用域或发生异常时,锁会被自动释放
  • ReentrantLock:需要显式释放锁,如果在 unlock() 方法之前发生异常,锁可能不会被释放,需要使用 try-finally 块或 try-with-resources 语句来确保锁被释放。

3、高级特性

  • ReentrantLock:提供了更多的高级特性,如公平锁、非公平锁、超时等待、可中断等待等。
  • synchronized:只支持非公平锁,不支持超时等待或可中断等待

4、条件变量

  • synchronized中可以使用wait(),notify(),notifyAll()进行线程间的
  • ReentrantLock配合Condition对象提供了强大的线程间协调能力,可以有多个Condition,每个Condition管理自己的等待集

5、性能

  • synchronized:基于 JVM 实现,使用了多种锁优化技术,如偏向锁、轻量级锁和重量级锁。这些技术使得 synchronized 在许多情况下性能接近或优于 ReentrantLock
  • ReentrantLock:基于 JDK 实现,使用了 CAS(Compare and Swap)原子操作进行锁的获取和释放。ReentrantLock 提供了更多高级功能,如公平锁、非公平锁、可中断的锁等待等。

总结

  • synchronized:适用于大多数基本的同步需求,提供了简洁的语法,自动释放锁,适合于简单的同步场景。
  • ReentrantLock:适用于需要更高级特性的场景,如公平锁、超时等待等,需要显式管理锁的获取和释放。
  • 如果竞争比较激烈,推荐ReentrantLock去实现,不存在锁升级概念。而synchronized是存在锁升级概念的,如果升级到重量级锁,是不存在锁降级的。

三、ReentrantLock使用场景

JDK 在并发包中, 使用 ReetrantLock 的地方有:

  1. CyclicBarrier

  2. DelayQueue

  3. LinkedBlockingDeque

  4. ThreadPoolExecutor

  5. ReentrantReadWriteLock

  6. StampedLock

1. 生产者消费者模式
应用场景:在消息队列或缓存系统中,生产者负责产生数据,消费者负责消费数据。为了确保数据的一致性和线程安全,可以使用 ReentrantLock

2. 文件上传下载系统
应用场景:在一个文件上传下载系统中,多个用户可能同时访问同一文件。为了保证文件的一致性和安全性,可以使用 ReentrantLock 来同步文件的读写操作

3. 线程池中的任务调度
应用场景:在线程池中,多个线程可能需要调度任务执行。使用 ReentrantLock 可以确保任务的正确调度和执行顺序。

4. 数据库连接池
应用场景:数据库连接池中需要管理多个数据库连接。使用 ReentrantLock 可以确保线程安全地获取和释放数据库连接

5. 限流器
应用场景:在高并发系统中,为了防止服务器过载,可以使用限流器来限制请求的速率。使用 ReentrantLock 可以确保限流逻辑的线程安全性
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/396825.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机卡换了上网的ip会改变吗

在数字化时代&#xff0c;互联网已成为我们日常生活不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们都离不开网络的支持。而每当涉及到网络连接&#xff0c;IP地址这一概念便显得尤为重要。IP地址不仅是设备在网络中的唯一标识&#xff0c;还关系到我们的网络体验…

Axure 变量魔法:揭秘局部与全局的动态协同

前言 在 Axure 的世界中&#xff0c;变量是连接设计者意图与用户行为的桥梁。 局部变量&#xff0c;以其独特的灵活性和针对性&#xff0c;允许我们在特定情境下快速响应用户的操作。 而全局变量&#xff0c;则以其广泛的覆盖范围&#xff0c;为跨页面的一致性和连贯性提供了…

最长路(有负权边)spfa

前言&#xff1a;这个题目中有负权重的边&#xff0c;狄克斯特拉算法坑定是用不了的&#xff0c;学一下spfa算法吧&#xff0c;发现就是bellman算法的队列优化 还有一个关键就是我们求最长的权重&#xff0c;我们要将边权重变为-的&#xff0c;最后答案取反就行 #define _CRT_S…

HTTP、HTTPS、SOCKS5三种协议特点

在互联网通信中&#xff0c;HTTP、HTTPS和SOCKS5是三种至关重要的协议&#xff0c;它们各自具有独特的特点和应用场景。本文将详细探讨这三种协议的特点&#xff0c;帮助读者更好地理解它们在网络通信中的作用。 一、HTTP协议特点 HTTP&#xff08;Hypertext Transfer Protoc…

使用 Prometheus 和 Grafana 监控 FastAPI 服务

在现代应用开发中&#xff0c;监控和可视化服务的运行状态和性能指标对于保证系统稳定性至关重要。本文将介绍如何使用 Prometheus 和 Grafana 对 FastAPI 服务进行监控和可视化&#xff0c;并展示如何通过 prometheus_fastapi_instrumentator 将 FastAPI 应用与 Prometheus 集…

【LVS】部署DR模式集群

一、配置实验环境 每台主机的防火墙和SELinux都要关掉 systemctl stop firewalld setenforce 0 1、client(eth0为nat模式) 配置好网卡IP和网关IP&#xff0c;然后重启网卡 nmcli connection reload nmcli connection up eth0 [rootclient ~]# cat /etc/NetworkManager/syst…

使用自定义注解和AOP解决登录校验问题

1、如果每次都从Redis获取token&#xff0c;会有很多冗余代码 2、使用面向切面编程的思想 在不改变源代码或者很少改变源代码的情况下&#xff0c;增强类的某些方法。 在业务代码之前设置 切入点 创建切面类&#xff0c;也就是比如登录校验的某些公共方法 切面类从切入点切入流…

json配置文件读入redis - 包含命令行解析示例

需要的取用&#xff0c;也是笔记&#xff0c;python argv经常会需要解析。类linux的命令行参数处理&#xff0c;这也是一个示例。 1.源码 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # 获取当前脚本文件所在目录的父目录&#xff0c;并构建相对路径 import os import …

【C语言篇】深入理解指针1

文章目录 内存和地址内存编址 指针变量和地址取地址操作符指针变量和解引用操作符指针变量指针变量类型解引用操作符指针变量的大小 指针变量类型的意义指针的解引用指针-整数void*指针 const修饰指针指针运算指针-整数指针-指针指针的关系运算 野指针野指针成因如何规避野指针…

嵌入式人工智能ESP32(2-GPIO之LED与按键)

1、ESP32引脚 ESP32 38脚与30脚的主要区别在于引脚数量和功能。‌选择38脚的ESP32开发板通常能提供更多的功能和更好的扩展性&#xff0c;‌适合需要连接多种传感器和外设以及进行复杂通信的应用。‌而30脚的ESP32开发板则可能更适合简单应用或成本敏感的项目。 SP32的引脚图…

STM32 | SPI+flash闪存(第十一天)W25Q128举例

点击上方"蓝字"关注我们 01、SPI 1.1 SPI概念理解 SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI 接口主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数…

MySQL笔记-基础篇(二):多表查询

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 前言 MySQL的多表查询是一项非常实用的数据库操作技术&#xff0c;它能够通过关联不同表中的数据来提供更加丰富和准确的信息。在实际应用中&#xff0c;数据通常不是孤立存在的&#xff0c;而是分布在多个…

Ubuntu(20.04)云主机SSH安全加固

1、新增SSH服务端口 #vim /etc/ssh/sshd_config 找到 #Port 22 去掉注释符&#xff0c;下面添加&#xff1a;Port [新端口] 2、本地防火墙放通 #ufw allow [新端口] #ufw reload //防火墙重新加载 #ufw status verbose //查询是否开放SSH新端口 3、腾讯云防火墙配…

提高PDF电子书的分辨率

解决方法出处 1. 安装ImageMagick brew install imagemagick brew install ghostscript2. 按流程进行 convert -density 600 your_pdf_filename.pdf output-%02d.jpg convert output*.jpg -normalize -threshold 80% final-%02d.jpg convert final*.jpg my_new_highcontras…

长语境窗口扩展:LongRoPE技术解析

人工智能咨询培训老师叶梓 转载标明出处 由于大模型高昂的微调成本、长文本的稀缺性&#xff0c;以及新引入的标记位置可能导致的灾难性值&#xff0c;目前扩展的语境窗口通常被限制在大约128k标记。为了克服这些限制&#xff0c;微软研究院的研究团队提出了一种名为LongRoPE的…

Apache OFBiz 曝出严重漏洞,允许预身份验证 RCE

近日&#xff0c;研究人员发现 Apache OFBiz 中存在一个新的关键漏洞&#xff0c;该漏洞是 Apache OFBiz 中的一个错误授权问题&#xff0c;被追踪为CVE-2024-38856。该漏洞影响 18.12.14 之前的版本&#xff0c;18.12.15 版本解决了该漏洞。 SonicWall 的安全研究员 Hasib Vh…

C语言常用的字符串函数(含模拟实现)

在使用C语言编写代码的时候&#xff0c;我们经常会用到一些库函数来实现一些平常难以实现的功能&#xff0c;今天我就为大家来分享一些我经常会用到的库函数&#xff0c;并且也会将他们的用法和部分的模拟实现函数分享给大家~ &#xff08;文中部分图片来取自strlen - C Refer…

优购电商小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;留言板管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;商品信息&#xf…

音频进阶学习一——模拟信号和数字信号

文章目录 前言|版本声明&#xff1a;山河君&#xff0c;未经博主允许&#xff0c;禁止转载 一、什么是模拟信号和数字信号信号模拟信号数字信号数字和模拟信号的区别一览 二、信号处理系统总结 前言 所有软件的运行都得益于硬件上的突破&#xff0c;数字信号是从40年前就开始高…

Qt 实战(9)窗体 | 9.2、QDialog

文章目录 一、QDialog1、基本概念2、常用特性2.1、模态与非模态2.2、数据交互 3、总结 前言&#xff1a; Qt框架中的QDialog类是一个功能强大且灵活的对话框控件&#xff0c;广泛应用于各种GUI&#xff08;图形用户界面&#xff09;应用程序中&#xff0c;用于处理用户输入、消…