面试遇到的问题2

Redisson的看门狗相关问题

首先要明确一点,看门狗机制的使用方式是:在加锁的时候不加任何参数,也就是:

RLock lock = redisson.getLock("myLock");
try {lock.lock();  // 阻塞式加锁// 业务逻辑...
} finally {lock.unlock(); // 确保在finally中释放锁
}  //这样子加锁会使用看门狗,也就是说这个锁会一直的续命下去,只要代码没有执行到 lock.unlock() 这一行

如果加了参数,那么和看门狗这个机制就完全不搭边,时间到了,强制放锁
通过 lock.lock(long leaseTime, TimeUnit unit) 指定锁的最大持有时间,超时后锁也会强制释放:

RLock lock = redisson.getLock("myLock");
try {// 设置锁超时时间为10秒,看门狗不会续期lock.lock(10, TimeUnit.SECONDS); // 业务逻辑...
} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}
}

那么说到这里就有一个很明显的问题,不加参数的时候,如果try里面有了死锁,或者一不小心写了 Thread.sleep(Integer.Max)这样的代码,那么这个锁就会一直不放,这显然是不对的,如果加了参数,强制放锁的时候,逻辑又没有执行完,就会有数据不一致,或者说并发的问题。怎么办?

问题核心

  • 不加超时时间(依赖看门狗):业务死锁时,锁会无限续期,导致其他线程永远无法获取锁(死锁风险)。
  • 加超时时间(强制释放):若业务未执行完但锁已超时释放,其他线程可能并发操作(数据不一致风险)。

解决方案

1. 合理设置超时时间(平衡点)
  • 原则

    • 超时时间(leaseTime)应 略大于 业务平均执行时间(如业务通常耗时 5秒,设置 leaseTime=10秒)。
    • 通过压测或监控统计业务耗时,动态调整超时时间。
  • 示例

    RLock lock = redisson.getLock("orderLock");
    try {// 设置超时时间为平均耗时的2倍lock.lock(10, TimeUnit.SECONDS); // 业务逻辑(假设通常耗时3~5秒)...
    } finally {lock.unlock();
    }
    
2. 异步续期 + 主动心跳检测
  • 适用场景:业务耗时波动大(如依赖外部服务响应)。

  • 实现方式

    1. 仍设置较短的 leaseTime(如 10秒)。
    2. 在业务代码中定期向 Redis 发送“心跳”(如每 5秒更新一次锁的时间戳)。
    3. 若业务卡死,心跳停止,锁超时释放。
  • 伪代码

    RLock lock = redisson.getLock("orderLock");
    try {lock.lock(10, TimeUnit.SECONDS);while (业务未完成) {// 业务逻辑...redisson.getBucket("lock:heartbeat:" + lockName).set(System.currentTimeMillis());Thread.sleep(5000); // 每5秒发送心跳}
    } finally {lock.unlock();
    }
    
3. 分段锁(减小锁粒度)
  • 原理:将大锁拆分为多个小锁,降低单个锁的持有时间。
  • 示例
    // 对订单ID分段加锁(如按订单ID哈希取模)
    int segment = orderId.hashCode() % 16;
    RLock lock = redisson.getLock("orderLock:" + segment);
    lock.lock(10, TimeUnit.SECONDS);
    
4. 兜底补偿机制
  • 场景:锁超时释放后,其他线程并发操作导致数据不一致。
  • 方案
    • 记录操作日志或版本号,通过定时任务检查并修复不一致数据。
    • 使用数据库乐观锁(如 UPDATE table SET value=newVal WHERE id=xxx AND version=oldVersion)。
5. 结合 tryLock 和熔断机制
  • 策略

    1. 尝试获取锁(带短时间等待)。
    2. 若获取失败,触发熔断(如返回“系统繁忙”)。
    3. 避免线程堆积导致雪崩。
  • 代码示例

    if (lock.tryLock(1, 10, TimeUnit.SECONDS)) { // 最多等待1秒,锁超时10秒try {// 业务逻辑...} finally {lock.unlock();}
    } else {throw new BusyException("系统繁忙,请重试");
    }
    

最佳实践总结

方案适用场景优点缺点
合理设置超时时间业务耗时稳定简单可靠需准确预估耗时
异步续期 + 心跳业务耗时波动大灵活控制锁生命周期实现复杂
分段锁高并发场景减少锁竞争需设计合理分段规则
兜底补偿对一致性要求不苛刻最终一致修复延迟
tryLock + 熔断高并发且允许短暂失败避免系统过载用户体验可能受影响

终极建议

  1. 优先使用 lock(leaseTime):根据业务 P99 耗时设置超时时间(如 P99=3秒,设 leaseTime=5秒)。
  2. 关键业务加监控:对锁持有时间超过阈值(如 80% leaseTime)触发告警。
  3. 设计幂等操作:即使锁超时导致并发,业务逻辑也能安全重试(如唯一键防重)。

:没有完美的方案,需根据业务容忍度权衡 “死锁风险”“并发风险”

Redisson中的锁是什么结构,存的是什么?底层实现是 setnx 么?

Redisson 分布式锁的底层实现

Redisson 的分布式锁(如 RLock)并非直接使用 Redis 的 SETNX 命令,而是基于 Lua 脚本Hash 结构 实现了一套更复杂的机制,支持可重入、锁续期(看门狗)、公平锁等高级特性。


1. 锁的存储结构

Redisson 的锁在 Redis 中存储为一个 Hash 结构,Key 的名称即锁的名称(如 myLock),Hash 的字段和值如下:

Hash FieldValue说明
UUID:threadId计数器(重入次数)锁的持有者标识(UUID 为客户端唯一ID,threadId 为线程ID),计数器表示重入次数。
moderedisson_lock标识这是一个 Redisson 锁。
leaseTime过期时间(毫秒)锁的租约时间(看门狗续期时会更新)。

示例 Redis 数据

HGETALL myLock
1) "b983c153-7421-469a-addf-8de0345aaedd:1"  # UUID:线程ID
2) "1"                                       # 重入次数
3) "mode"
4) "redisson_lock"
5) "leaseTime"
6) "30000"                                   # 30秒过期时间

2. 加锁流程(核心 Lua 脚本)

Redisson 加锁的核心是通过 Lua 脚本 保证原子性,逻辑如下:

(1) 加锁脚本(lock.lua

-- 参数:KEYS[1]=锁名称, ARGV[1]=锁过期时间, ARGV[2]=客户端UUID:线程ID
-- 1. 检查锁是否存在
if (redis.call('exists', KEYS[1]) == 0) then-- 锁不存在,直接加锁redis.call('hset', KEYS[1], ARGV[2], 1);  -- 设置 Hash 字段(UUID:threadId: 1)redis.call('pexpire', KEYS[1], ARGV[1]);  -- 设置过期时间return nil;                               -- 返回 nil 表示加锁成功
end;-- 2. 锁已存在,检查是否当前线程持有
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then-- 可重入锁:增加计数器redis.call('hincrby', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]);  -- 续期return nil;
end;-- 3. 锁被其他线程持有,返回剩余存活时间(TTL)
return redis.call('pttl', KEYS[1]);

关键点

  • 原子性:通过 Lua 脚本保证 exists + hset + pexpire 的原子操作。
  • 可重入:同一个线程多次加锁时,hincrby 增加计数器。
  • 锁竞争:如果锁被其他线程持有,返回剩余 TTL,客户端会循环尝试。

(2) 看门狗续期机制

如果使用 lock() 不加超时时间,Redisson 会启动一个 看门狗线程(Watchdog),默认每 10秒 检查锁是否仍被当前线程持有,如果是,则续期 30秒

-- 续期脚本
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) thenredis.call('pexpire', KEYS[1], ARGV[1]); -- 重置过期时间return 1;
end;
return 0;

3. 解锁流程

解锁时,Redisson 会先检查锁是否仍被当前线程持有,然后减少重入计数或直接删除锁:

-- 参数:KEYS[1]=锁名称, ARGV[1]=过期时间, ARGV[2]=客户端UUID:线程ID
-- 1. 检查锁是否属于当前线程
if (redis.call('hexists', KEYS[1], ARGV[2]) == 0) thenreturn nil;
end;-- 2. 减少重入计数
local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1);
if (counter > 0) then-- 仍持有锁(重入情况),续期redis.call('pexpire', KEYS[1], ARGV[1]);return 0;
else-- 完全释放锁,删除 Keyredis.call('del', KEYS[1]);-- 发布解锁消息(通知其他等待线程)redis.call('publish', KEYS[2], ARGV[3]);return 1;
end;

4. 与 SETNX 的区别

对比项Redisson 锁SETNX 锁
数据结构Hash(存储客户端ID、重入次数)String(简单 Key-Value)
可重入✅ 支持(通过计数器)❌ 不支持
自动续期✅ 看门狗机制❌ 需手动实现
公平锁✅ 支持(通过 Redis 队列)❌ 不支持
原子性✅ Lua 脚本保证⚠️ 需配合 EXPIRE(非原子)

5. 总结

  1. Redisson 锁的底层是 Hash 结构,存储 客户端ID:线程ID重入次数
  2. 加锁/解锁通过 Lua 脚本保证原子性,而非简单的 SETNX
  3. 看门狗机制 自动续期,避免锁过期。
  4. 支持可重入、公平锁等高级特性,比 SETNX 更强大。

适用场景

  • 需要可重入锁 → Redisson
  • 简单互斥锁 → SETNX + EXPIRE(但需自行处理原子性问题)

看门狗是如何知道某个线程是否存活,或者说某个线程是否还在执行?

Redisson 看门狗(Watchdog)如何检测线程存活?

Redisson 的看门狗机制并不直接监控 JVM 线程是否存活,而是通过以下方式间接判断业务是否仍在执行,从而决定是否续期锁:


1. 看门狗的核心逻辑

(1) 锁续期的触发条件

  • 当使用 lock() 不加超时时间时,Redisson 会启动一个后台线程(看门狗),默认每隔 10秒 执行一次续期检查。
  • 续期条件
    1. 锁仍存在于 Redis 中(未被手动释放或过期)。
    2. 锁的持有者仍是当前客户端和线程(通过 Redis Hash 中的 UUID:threadId 字段验证)。

(2) 续期流程

  1. 检查锁归属
    看门狗通过 Lua 脚本检查 Redis 中的锁是否仍由当前客户端(UUID)和线程(threadId)持有:
    if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then  -- 检查UUID:threadId是否存在redis.call('pexpire', KEYS[1], ARGV[1]);          -- 续期锁return 1;
    end;
    return 0;
    
  2. 续期或终止
    • 如果锁仍属于当前线程,则重置过期时间为 30秒(默认)。
    • 如果锁已被释放或归属变更,看门狗停止续期。

2. 如何判断业务线程是否仍在执行?

看门狗不直接监控业务线程状态,而是通过以下间接机制推断:

(1) JVM 线程存活间接关联

  • 锁释放的触发条件

    • 业务线程正常执行完成后,调用 lock.unlock(),删除 Redis 中的锁。
    • 如果业务线程崩溃或阻塞,unlock() 不会被调用,锁会因看门狗续期而长期存在。
  • 看门狗续期的隐含假设

    • 只要 JVM 进程存活,且持有锁的线程未调用 unlock(),看门狗就会认为业务仍在执行(即使线程实际已阻塞或死锁)。

(2) 异常情况处理

  • 线程崩溃或 JVM 退出

    • 看门狗线程是后台守护线程(Daemon Thread),如果 JVM 进程退出,看门狗会停止运行,锁最终会在 Redis 中自然过期(默认 30秒后释放)。
    • 无需依赖线程存活检测,依赖 Redis 的过期机制兜底。
  • 网络分区或 Redis 不可用

    • 看门狗无法连接 Redis 时,续期失败,锁会在过期后自动释放。

3. 为什么不用 Java 线程监控?

  1. 分布式环境限制
    • Redisson 是分布式锁,可能跨多个 JVM 或机器,无法直接监控其他 JVM 的线程状态。
  2. 复杂度与可靠性
    • 线程监控需依赖 JVM 的 Thread.isAlive(),但分布式场景下不可靠(如网络分区时误判)。
  3. Redis 是唯一信源
    • 所有锁状态由 Redis 存储,看门狗只需关心 Redis 中的数据,无需跨进程协调。

4. 关键结论

问题Redisson 的解决方案
如何知道线程是否存活?不直接检测,而是通过锁是否被主动释放(unlock())判断。未释放则续期。
线程崩溃后锁何时释放?依赖 Redis 的 Key 过期机制(默认 30秒),看门狗停止续期后锁自动释放。
看门狗如何保证不续死锁?业务必须显式调用 unlock(),否则需设置超时时间(lock(10, TimeUnit.SECONDS))避免无限续期。

5. 最佳实践

  1. 避免依赖看门狗无限续期
    • 优先使用 lock(leaseTime, unit) 设置合理的超时时间,即使业务卡死,锁也会自动释放。
    lock.lock(10, TimeUnit.SECONDS); // 明确指定超时时间
    
  2. 确保锁被释放
    • unlock() 放在 finally 块中,防止异常导致锁泄漏。
    try {lock.lock();// 业务逻辑...
    } finally {lock.unlock();
    }
    
  3. 监控长耗时锁
    • 对持有时间超过阈值的锁触发告警(如通过 Redisson 的 RLock.getRemainingLeaseTime())。

总结

Redisson 的看门狗通过 定期续期 Redis 锁检查锁归属 间接判断业务是否执行中,而非直接监控线程状态。
设计核心思想分布式锁的安全性应完全由 Redis 保证,避免依赖 JVM 层面的不可靠检测。

也就是说看门狗其实和 jvm中线程完全没关系,只不过是运行借助jvm运行的一个守护线程,这个守护线程只和redis通信,不和jvm中其他线程做任何的数据交互

核心总结

  1. 看门狗的本质

    • 看门狗线程的启动时机,全局看门狗线程池:Redisson 在客户端初始化时(即 RedissonClient 创建时)会启动一个名为redisson-timeout 的守护线程池。这个线程池并非专为看门狗设计,而是用于处理所有需要超时控制的异步任务(包括但不限于锁的续期)。

    • 看门狗线程的懒加载:只有在首次使用无超时锁(lock())时,才会从该线程池中分配一个线程作为专属看门狗,用于定期续期该锁。如果只用带超时的锁(lock(leaseTime, unit)),则不会分配看门狗线程。

    • 仅与 Redis 交互,通过定期执行 Lua 脚本检查/续期锁,完全不感知 JVM 内其他线程的状态(如是否阻塞、崩溃)。

  2. 与 JVM 线程的关系

    • 不依赖线程监控:看门狗不会检查业务线程(如 Thread.isAlive()),也不与业务线程直接通信。
    • 仅依赖 Redis 数据:通过 Redis 中锁的 UUID:threadId 字段是否存在,间接判断是否续期。
  3. 设计优势

    • 解耦:分布式锁的安全性完全由 Redis 保证,与 JVM 线程生命周期无关。
    • 轻量:避免复杂的线程监控,仅通过 Redis 的键存在性检查实现高效续期。
    • 可靠:即使业务线程死锁,锁最终会因看门狗停止(JVM 退出)或 Redis 过期而释放。

关键流程验证

场景看门狗行为锁的最终状态
业务线程正常执行并解锁看门狗检测到 unlock() 删除 Redis 中的 UUID:threadId,停止续期。立即释放
业务线程死锁/阻塞看门狗持续续期(因 UUID:threadId 仍在 Redis 中),直到 JVM 退出或手动干预。可能长期占用(需设置超时时间避免)
JVM 崩溃看门狗线程终止,无人续期,Redis 中的锁自然过期(默认30秒)。自动释放

代码层面验证

1. 看门狗线程的启动

Redisson 客户端初始化时,会创建一个全局的 Watchdog 守护线程(单例):

public class RedissonClient {private final Watchdog watchdog = new Watchdog(); // 守护线程
}
2. 续期任务提交

业务线程加锁时,向看门狗注册续期任务(仅记录锁名称和 UUID:threadId):

lock.lock(); // 无参加锁
// 内部逻辑:
watchdog.scheduleRenewal("myLock", "uuid:threadId");
3. 看门狗的工作内容

看门狗线程循环检查任务队列,通过 Redis 的 hexists 命令验证锁归属:

// 伪代码
while (!Thread.interrupted()) {for (LockTask task : tasks) {String result = redis.eval("if (redis.call('hexists', KEYS[1], ARGV[1]) == 1) then " +"   redis.call('pexpire', KEYS[1], ARGV[2]); " +"   return 1; " +"end; " +"return 0;",Collections.singletonList(task.getLockName()),task.getUUIDThreadId(), "30000" // 续期30秒);if (result.equals("0")) {removeTask(task); // 锁已失效,移出任务队列}}Thread.sleep(10000); // 默认10秒检查一次
}

常见问题解答

Q1: 如果业务线程卡死,看门狗会一直续期吗?

是的,因为看门狗只检查 Redis 中的 UUID:threadId 是否存在,不感知线程状态。需通过 lock(leaseTime, unit) 设置超时时间避免无限续期。

Q2: 为什么看门狗不直接监控业务线程?
  • 分布式限制:无法跨 JVM 监控线程(如微服务场景)。
  • 复杂度:线程监控需处理 JVM 崩溃、网络分区等边界条件,不可靠。
Q3: 看门狗会拖累 Redis 性能吗?
  • 低开销:续期是低频操作(默认10秒一次),且 Lua 脚本轻量。
  • 单线程优化:所有锁的续期由同一个看门狗线程批量处理。

最佳实践建议

  1. 永远设置超时时间
    lock.lock(10, TimeUnit.SECONDS); // 避免死锁导致无限续期
    
  2. 确保解锁
    try {lock.lock();// 业务逻辑...
    } finally {lock.unlock(); // 必须释放!
    }
    
  3. 监控长耗时锁
    long remainTime = lock.remainTimeToLive();
    if (remainTime > THRESHOLD) {log.warn("锁持有时间过长:{}", lock.getName());
    }
    

总结

看门狗是一个纯粹的 Redis 通信守护线程,通过定期检查 Redis 中的锁归属决定是否续期,与 JVM 线程完全解耦。
这种设计实现了分布式锁的高可靠性低侵入性,但需开发者合理设置超时时间避免死锁。

看门狗是多久检查一次上锁的hash?续命多久?

在 Redisson 中,看门狗(Watchdog) 的检查与续期机制是锁续期的核心逻辑,具体行为如下:


1. 看门狗的检查与续期频率

  • 默认检查间隔每 10 秒lockWatchdogTimeout / 3)检查一次锁状态。
    • lockWatchdogTimeout 默认值为 30 秒(可通过 Config.lockWatchdogTimeout 调整)。
    • 计算公式:续期间隔 = lockWatchdogTimeout / 330s / 3 = 10s
  • 续期逻辑:每次检查时,如果锁仍被当前线程持有,则将锁的过期时间重置为 lockWatchdogTimeout(默认 30 秒)

2. 关键源码解析

(1)续期任务调度(scheduleExpirationRenewal
// RedissonLock.java
protected void scheduleExpirationRenewal(long threadId) {// 每 (lockWatchdogTimeout / 3) 秒执行一次续期Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) {// 执行续期逻辑renewExpiration();}}, lockWatchdogTimeout / 3, TimeUnit.MILLISECONDS); // 默认 10 秒
}
(2)续期操作(renewExpiration
// RedissonLock.java
void renewExpiration() {// 通过 Lua 脚本重置锁的 TTLRFuture<Boolean> future = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +  // 重置 TTL 为 lockWatchdogTimeout"return 1; " +"end; " +"return 0;",Collections.singletonList(getName()),internalLockLeaseTime,  // ARGV[1]: lockWatchdogTimeout(默认 30000 毫秒)getLockName(threadId)   // ARGV[2]: 客户端标识);// 递归调用,实现周期性续期future.onComplete((res, e) -> {if (e != null) {return;}if (res) {scheduleExpirationRenewal(threadId); // 继续下一次续期}});
}

3. 续期流程总结

  1. 首次加锁(无超时)
    • 锁在 Redis 中的 TTL 默认设为 lockWatchdogTimeout(30 秒)。
    • 看门狗在 10 秒 后首次检查,若锁仍存在,则重置 TTL 为 30 秒
  2. 周期性续期
    • 10 秒 检查一次,若锁未被释放,则再次续期 30 秒
    • 直到显式调用 unlock() 或客户端崩溃(此时续期任务停止,Redis 自动过期)。

4. 关键配置参数

参数默认值说明
lockWatchdogTimeout30000 ms锁的默认存活时间(无超时锁的初始 TTL 和续期时长)。可通过 Config 自定义。
实际续期间隔10000 mslockWatchdogTimeout / 3,即默认 10 秒。

5. 注意事项

  1. 仅对无超时锁生效
    显式指定 leaseTime 的锁(如 lock(10, SECONDS))不会触发看门狗续期。
  2. 避免长时间阻塞
    如果业务逻辑执行时间超过 lockWatchdogTimeout,且未及时续期,锁可能因 Redis TTL 到期而失效。
  3. 调整参数需谨慎
    • 增大 lockWatchdogTimeout:降低续期频率,但锁释放延迟可能增加。
    • 减小 lockWatchdogTimeout:提高续期频率,但增加 Redis 和网络负载。

6. 示例场景

// 无超时锁(启用看门狗)
RLock lock = redisson.getLock("myLock");
lock.lock(); // 看门狗每 10 秒续期一次,每次续 30 秒
try {// 执行业务逻辑(可能耗时较长)
} finally {lock.unlock(); // 主动释放,停止续期
}

通过这种机制,Redisson 确保了无超时锁的长期持有安全性,同时避免了因客户端崩溃导致的死锁问题

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

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

相关文章

Linux—进程概念与理解

目录 1.冯诺依曼体系结构 小结&#xff1a; 2.操作系统 概念&#xff1a; 结构示意图&#xff1a; 理解操作系统&#xff1a; 用户使用底层硬件层次图&#xff1a;​编辑 3.进程 概念 结构示意图 task_ struct内容分类 典型用法示例 观察进程: 了解 PID PPID 查…

LeetCode 面试经典 150_数组/字符串_买卖股票的最佳时机(7_121_C++_简单)(贪心)

LeetCode 面试经典 150_数组/字符串_买卖股票的最佳时机&#xff08;7_121_C_简单&#xff09;题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;贪心算法&#xff09;&#xff1a;代码实现代码实现&#xff08;思路一&…

Ubuntu 18.04 repo sync报错:line 0: Bad configuration option: setenv

repo sync时报 line 0: Bad configuration option: setenv因为 Ubuntu 18.04 默认的 openssh-client 是 7.6p1&#xff0c;还不支持 setenv&#xff0c;但是.repo/repo/ssh.py 脚本中明确地传入了 SetEnv 参数给 ssh&#xff0c;而你的 OpenSSH 7.6 不支持这个参数。需要按如下…

bug记录-stylelint

BUG1不支持Vue文件内联style样式解决&#xff1a; "no-invalid-position-declaration": null

前端开发(HTML,CSS,VUE,JS)从入门到精通!第一天(HTML5)

一、HTML5 简介1&#xff0e;HTML全称是 Hyber Text Markup Language&#xff0c;超文本标记语言&#xff0c;它是互联网上应用最广泛的标记语言&#xff0c;简单说&#xff0c;HTML 页面就等于“普通文本HTML标记&#xff08;HTML标签&#xff09;“。2&#xff0e;HTML 总共经…

智慧收银系统开发进销存:便利店、水果店、建材与家居行业的—仙盟创梦IDE

在数字化转型的浪潮中&#xff0c;收银系统已不再局限于简单的收款功能&#xff0c;而是成为企业进销存管理的核心枢纽。从便利店的快消品管理到建材家居行业的大宗商品调度&#xff0c;现代收银系统通过智能化技术重塑了传统商业模式。本文将深入探讨收银系统在不同行业进销存…

三维扫描相机:工业自动化的智慧之眼——迁移科技赋能智能制造新纪元

在当今工业4.0时代&#xff0c;自动化技术正重塑生产流程&#xff0c;而核心工具如三维扫描相机已成为关键驱动力。作为工业自动化领域的“智慧之眼”&#xff0c;三维扫描相机通过高精度三维重建能力&#xff0c;解决了传统制造中的效率瓶颈和精度痛点。迁移科技&#xff0c;自…

Jmeter的元件使用介绍:(九)监听器详解

监听器主要是用来监听脚本执行的取样器结果。Jmeter的默认监听器有&#xff1a;查看结果树、聚合报告、汇总报告、用表格查看结果&#xff0c;断言结果、图形结果、Beanshell监听器、JSR223监听器、比较断言可视化器、后端监听器、邮件观察器&#xff0c;本文介绍最常用的监听器…

联通元景万悟 开源,抢先体验!!!

简介&#xff1a; 元景万悟智能体平台是一款面向企业级场景的一站式、商用license友好的智能体开发平台&#xff0c;是业界第一款go语言&#xff08;后端&#xff09;开发的智能体开发平台&#xff08;7月19日&#xff09;&#xff0c;coze studio开源是7月26日&#xff0c;同时…

Git之本地仓库管理

一.什么是Git在学习工作中&#xff0c;我们经常会遇到改文档的场景。一个文档可能会被我们修改多次&#xff0c;而最终真正使用的可能是最先的几版。而如果我们直接在原文档上修改&#xff0c;就会导致无法找到最先的几次。这也就导致我们要对我们所有的版本进行维护&#xff0…

Go再进阶:结构体、接口与面向对象编程

&#x1f680; Go再进阶&#xff1a;结构体、接口与面向对象编程 大家好&#xff01;在前两篇文章中&#xff0c;我们深入学习了Go语言的流程控制语句以及数组和切片的使用并且还对Go 语言的核心知识点进行了补充讲解&#xff0c;这些知识让我们能够编写出更为复杂和灵活的程序…

Python入门第六课:现代开发与前沿技术

异步编程(asyncio) 1. 协程基础 import asyncio import time# 定义协程函数 async def say_after(delay, message):await asyncio.sleep(delay)print(message)# 主协程 async def main():print(f"开始时间: {time.strftime(%X)}")# 顺序执行await say_after(2, 你…

STM32移植LVGL9.2.1教程

一、环境说明 &#xff08;1&#xff09;开发板&#xff1a;STM32F401RCT6核心板&#xff08;网上很多&#xff0c;价格只有几块钱&#xff09; &#xff08;2&#xff09;屏幕&#xff1a;2.8寸spi屏gt911触摸 转接板&#xff08;某宝有卖&#xff0c;没有推广自行搜索&…

python学智能算法(二十九)|SVM-拉格朗日函数求解中-KKT条件理解

【1】引言 前序学习阶段中&#xff0c;我们掌握了最佳分割超平面对应的构造拉格朗日函数极值为&#xff1a; L(w,b,α)∑i1mαi−12∑i,j1mαiαjyiyjxiTxjL(w,b,\alpha)\sum_{i1}^{m}\alpha_{i}-\frac{1}{2}\sum_{i,j1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}x_{i}^{T}x_{j}L(w,…

大模型应用开发1-认识大模型

1.基础概念 1.1 AI的概念&#xff1a; AI&#xff0c;⼈⼯智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;使机器能够像⼈类⼀样思考、学习和解决问题的技术。AI发展⾄今⼤概可以分为三个阶段&#xff1a;其中&#xff0c;深度学习领域的自然语言处理&#…

Linux 远程连接解析:SSH 协议理论与应用

Linux 远程连接解析&#xff1a;SSH 协议理论与应用在网络互联的时代&#xff0c;远程管理服务器已成为常态。SSH&#xff08;Secure Shell&#xff09;作为一种安全的网络协议&#xff0c;凭借其加密机制和灵活的功能&#xff0c;成为 Linux 系统远程操作的事实标准。本文将从…

ubuntu22.04系统入门 linux入门 简单命令基础复习 实现以及实践

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址&#xff1a;星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 兑换码要是过期了&#xff0c;可以私信我获取最新兑换码&#xff01;&a…

软考中级-信息安全工程师-每日一学(1)

前提概要本文章主要用于分享软考中级-信息安全工程师-学习&#xff0c;以下是一些个人理解&#xff0c;请大家结合参考其他文章中的相关信息及个人经验进行归纳和补充&#xff0c;内容会存在一定错误&#xff0c;希望读者多多评论批评&#xff0c;本人在此先说感谢啦。1.密码学…

EEG手工特征提取总结

目录一、引言EEG信号简介EEG特征提取的重要性本次汇报目的与内容概述二、EEG信号核心特征时域特征 (Time-Domain Features)频域特征 (Frequency-Domain Features)三、EEG信号高级特征时频域特征 (Time-Frequency Domain Features)空间域特征 (Spatial-Domain Features)复杂动力…

React 路由守卫

下面&#xff0c;我们来系统的梳理关于 React Router 路由守卫 的基本知识点&#xff1a;一、路由守卫概述 1.1 什么是路由守卫 路由守卫是一种在用户导航到特定路由之前或离开特定路由时执行逻辑的机制。它允许开发者控制用户访问权限、验证条件或执行数据预加载等操作。 1.2 …