当前位置: 首页 > article >正文

ReentranLock手写

ReentranLock手写

整体概述

MiniLock 是一个自定义的锁实现,模拟了 Java ReentrantLock 的公平锁机制。公平锁的核心思想是“先来后到”,即线程按照请求锁的顺序依次获取锁,避免线程饥饿。代码使用了以下关键组件:

  1. state: 表示锁的状态,0 表示未被锁定,>0 表示已被锁定。
  2. exclusiveOwerThread: 记录当前持有锁的线程(独占模式)。
  3. Node 队列: 使用双向链表维护等待锁的线程队列,head 是持有锁的线程节点,tail 是队列尾部。
  4. LockSupport: 用于线程的阻塞(park)和唤醒(unpark)。

代码实现了 Lock 接口的 lock()unlock() 方法,并通过 tryAcquiretryRelease 等辅助方法实现锁的获取和释放逻辑。


关键字段和类

  1. state

    • 类型:volatile int
    • 作用:表示锁的状态。
      • 0:未被锁定。
      • >0:已被锁定,值表示重入次数。
    • 使用 volatile 确保多线程下的可见性。
  2. exclusiveOwerThread

    • 类型:Thread
    • 作用:记录当前持有锁的线程,确保锁的独占性。
  3. Node

    • 内部静态类,用于表示等待队列中的节点。
    • 字段:
      • prev:前一个节点。
      • next:后一个节点。
      • thread:当前节点对应的线程。
    • 作用:构建一个双向链表队列,保存等待获取锁的线程。
  4. headtail

    • 类型:Node
    • 作用:
      • head:队列头部,总是指向当前持有锁的线程节点。
      • tail:队列尾部,指向最后一个等待的线程节点。

核心方法解析

1. lock()
public void lock() {acquire(1);
}
  • 作用:获取锁的入口方法。
  • 参数 1 表示获取锁的次数(支持重入时会累加)。
  • 逻辑:调用 acquire(1) 开始抢占锁。
2. acquire(int arg)
private void acquire(int arg) {if (!tryAcquire(arg)) {Node node = addWaiter();acquireQueued(node, arg);}
}
  • 作用:尝试获取锁,如果失败则将线程加入等待队列并阻塞。
  • 步骤:
    1. 调用 tryAcquire(arg) 尝试抢占锁。
      • 如果成功,直接返回。
      • 如果失败,继续执行。
    2. 调用 addWaiter() 将当前线程封装成 Node 并加入队列。
    3. 调用 acquireQueued(node, arg) 阻塞线程并等待获取锁。
3. tryAcquire(int arg)
private boolean tryAcquire(int arg) {if (state == 0) {if (!hasQueuedPredecessor() && casState(0, arg)) {exclusiveOwerThread = Thread.currentThread();return true;}} else if (Thread.currentThread().equals(exclusiveOwerThread)) {int c = getState();c += arg;state = c;}return false;
}
  • 作用:尝试获取锁(非阻塞)。
  • 返回值:
    • true:抢占成功。
    • false:抢占失败。
  • 逻辑:
    1. 锁未被占用(state == 0
      • 检查队列中是否有等待线程(hasQueuedPredecessor())。
      • 如果没有等待线程且 casState(0, arg) 成功(原子地将 state0 改为 arg),则:
        • 设置 exclusiveOwerThread 为当前线程。
        • 返回 true
    2. 锁已被占用且当前线程是持有者
      • 如果当前线程已经持有锁(支持重入),则:
        • state 增加 arg(重入计数)。
        • 返回 true
    3. 其他情况
      • 返回 false,表示抢锁失败。
4. hasQueuedPredecessor()
private boolean hasQueuedPredecessor() {Node h = head;Node t = tail;Node s;return h != t && ((s = h.next) == null || s.thread == Thread.currentThread());
}
  • 作用:判断当前线程是否需要排队。
  • 返回值:
    • true:队列中有等待线程,当前线程需要排队。
    • false:队列为空或当前线程是 head.next(有资格抢锁)。
  • 逻辑:
    • h != t:队列不为空。
    • (s = h.next) == null:队列刚初始化或正在调整。
    • s.thread == Thread.currentThread():当前线程是队列中的下一个线程。
5. addWaiter()
private Node addWaiter() {Node newNode = new Node(Thread.currentThread());Node pred = tail;while (pred != null) {newNode.prev = pred;if (casTail(pred, newNode)) {pred.next = newNode;return newNode;}}enq(newNode);return newNode;
}
  • 作用:将当前线程加入等待队列。
  • 逻辑:
    1. 创建一个新节点 newNode,绑定当前线程。
    2. 如果 tail != null,尝试将新节点加入队列尾部:
      • 设置 newNode.prev = pred
      • 使用 casTail 原子更新 tail
      • 如果成功,更新 pred.next 并返回。
    3. 如果 tail == null 或 CAS 失败,调用 enq(newNode) 自旋入队。
6. enq(Node node)
private void enq(Node node) {for (;;) {if (tail == null) {if (casHead(new Node())) {tail = head;}} else {Node pred = tail;node.prev = pred;if (casTail(pred, node)) {pred.next = node;return;}}}
}
  • 作用:自旋方式将节点加入队列,确保入队成功。
  • 逻辑:
    1. 队列为空(tail == null
      • 创建一个空的 head 节点并尝试通过 CAS 设置。
      • 设置 tail = head
    2. 队列非空
      • 将新节点加入 tail 后,通过 CAS 更新 tail
      • 更新链表指针后返回。
7. acquireQueued(Node node, int arg)
private void acquireQueued(Node node, int arg) {for (;;) {Node pred = node.prev;if (pred == head && tryAcquire(arg)) {setHead(node);pred.next = null;return;}LockSupport.park();}
}
  • 作用:阻塞当前线程,直到成功获取锁。
  • 逻辑:
    • 自旋检查:
      • 如果当前节点的前驱是 headtryAcquire 成功:
        • 将当前节点设为 head
        • 断开原 headnext 指针。
        • 返回。
      • 否则,调用 LockSupport.park() 阻塞线程。
8. unlock()
public void unlock() {release(1);
}
  • 作用:释放锁。
  • 逻辑:调用 release(1) 释放锁。
9. release(int arg)
private void release(int arg) {if (tryRelease(arg)) {Node head = getHead();if (head.next != null) {unparkSuccessor(head);}}
}
  • 作用:释放锁并唤醒队列中的下一个线程。
  • 逻辑:
    • 调用 tryRelease(arg) 尝试释放锁。
    • 如果完全释放成功(state == 0)且队列中有等待线程:
      • 调用 unparkSuccessor(head) 唤醒 head.next 的线程。
10. tryRelease(int arg)
private boolean tryRelease(int arg) {int c = getState() - arg;if (getExclusiveOwerThread() != Thread.currentThread()) {throw new RuntimeException("fuck error");}if (c == 0) {exclusiveOwerThread = null;state = 0;return true;}state = c;return false;
}
  • 作用:尝试释放锁。
  • 返回值:
    • true:锁完全释放。
    • false:锁部分释放(重入计数减少)。
  • 逻辑:
    • 检查当前线程是否是锁持有者,否则抛出异常。
    • 计算新状态 c = state - arg
    • 如果 c == 0
      • 清空 exclusiveOwerThread
      • 设置 state = 0
      • 返回 true
    • 否则:
      • 更新 state = c
      • 返回 false
11. unparkSuccessor(Node node)
private void unparkSuccessor(Node node) {Node next = node.next;if (next != null && next.thread != null) {LockSupport.unpark(next.thread);}
}
  • 作用:唤醒队列中的下一个线程。
  • 逻辑:
    • 获取 node.next
    • 如果存在且绑定了线程,调用 LockSupport.unpark() 唤醒。

公平锁的实现要点

  1. 先来后到
    • 通过 hasQueuedPredecessor() 确保只有队列为空或当前线程是 head.next 时才能抢锁。
  2. 可重入性
    • 如果当前线程已持有锁,tryAcquire 会增加 state 值,而不是阻塞。
  3. 线程安全
    • 使用 CAS 操作(casStatecasHeadcasTail)保证并发下的原子性。
    • volatile 修饰 state 确保可见性。
  4. 阻塞与唤醒
    • 使用 LockSupport.park()unpark() 实现线程的挂起和唤醒。

注意事项

  1. CAS 实现缺失
    • 代码中的 casStatecasHeadcasTail 方法仅返回 true,没有实现真正的原子操作。实际中需要使用 UnsafeAtomicInteger
  2. 边界情况
    • tryRelease 中存在错误:state = 0 应为 state = c,否则非完全释放时会错误清零。
  3. 性能
    • 自旋操作(enqacquireQueued)可能在高并发下消耗 CPU。

总结

这段代码通过双向链表队列和状态管理实现了一个公平、可重入的独占锁。核心逻辑是:

  • 获取锁:尝试抢占 -> 失败则入队 -> 阻塞等待。
  • 释放锁:减少重入计数 -> 完全释放则唤醒队列中的下一个线程。
    尽管实现简化和部分功能未完善(如 CAS),但它很好地展示了 ReentrantLock 公平锁的核心原理。

完整代码:

import java.util.concurrent.locks.LockSupport;/***          可以重入的、独占的公平锁** */public class MiniLock implements Lock {/***  锁的是资源*  0 -> 未加锁状态* !0 -> 加锁状态*/private volatile int state;// 记录当前获取锁的线程  独占模式 在同一时刻只有一个线程可以持有锁,其他线程未获取到就会被阻塞public Thread exclusiveOwerThread;/***  需要有两个引用来维护被阻塞线程的队列*  1. head:队列头结点,头节点就是当前占有锁的线程*  2. tail:队列的尾节点*/private Node head;private Node tail;public Node getHead() {return head;}public int getState() {return state;}public Node getTail() {return tail;}public void setHead(Node head) {this.head = head;}public Thread getExclusiveOwerThread() {return exclusiveOwerThread;}// 用一个链表队列保存要维护的线程static final class Node {Node prev;  // 前置节点Node next;  // 后置节点Thread thread;  // 当前node节点的线程public Node(Thread thread) {this.thread = thread;}public Node() {}}/***  目的:获取锁,假设当前锁被占用,会阻塞调用者的线程,直到抢占到锁为止**  模拟公平锁:先来后到**  lock过程:*  情景1:线程进入 发现 state == 0,直接抢锁*  情景2:线程进入 发现 state > 0,需要进入锁的阻塞队列中* */@Overridepublic void lock() {acquire(1);}/*** 争资源* 1. 尝试抢占锁,成功占到 就返回* 2. 抢占失败 放入队列 将线程阻塞*/private void acquire(int arg){if(!tryAcquire(arg)){// 1. 如果抢锁失败 把自己打包成一个node 进入队列Node node = addWaiter();// 2. 然后阻塞acquireQueued(node, arg);}}private void acquireQueued(Node node, int arg){// 只有当前node成功获取到锁 才会跳出自旋for(;;){// 什么时候会被唤醒获取到锁  head.next 先来后到Node pred = node.prev;if(pred == head/*是否有资格来抢锁*/ && tryAcquire(arg)){// 竞争成功// 1. 把自己设置成head节点// 2. 原先的head出队setHead(node);pred.next = null;return;}// 枪锁失败LockSupport.park();}}/***  尝试抢占锁失败后:*  1. 把当前的线程封装成Node 进入阻塞队列*  2. 将当前线程park掉 处于挂起状态**  唤醒之后需要:*  1. 先检测一下自己是否为 head.next 节点(只有head.next有权限抢占锁)*  2. 枪锁*   成功:成功后先将自己设置为head节点 并且 之前的head出队列*   失败:失败后继续 park 等待下一次唤醒*//*** 将当前线程入队列 并且返回当前线程节点** addWaiter 保证入队成功* @return*/private Node addWaiter(){Node newNode = new Node(Thread.currentThread());// 进入阻塞队列// 1. 先找到前置节点 pred 去更新前置节点  2. cas更新tail节点  3. pred.next = newNodeNode pred = tail;while(pred != null){newNode.prev = pred;if (casTail(pred, newNode)) {       // if cas 成功pred.next = newNode;return newNode;}}// 情况讨论:// 1. tail == null// 2. cas tail失败// 自旋入队enq(newNode);return newNode;}/***  自旋入队, 只有成功后才可以返回*  tail == null*  cas tail 失败* @param node*/private void enq(Node node){for(;;){// 队列为空 当前持有锁的线程并没有入队 并且没设置任何node// 作为后驱节点,需要为第一个线程完成node入队的情况,保持(head 节点任何时候都是占用锁的线程)这个事情if (tail == null) {// 说明成功了 当前持有锁的线程补充head线程成功了if(casHead(new Node())){tail = head;// 这里不返回}} else {Node pred = tail;node.prev = pred;if(casTail(pred, node)){pred.next = node;return;}}}}/*** 尝试获取锁 不会阻塞线程* true - 抢占成功* false - 抢占失败*/private boolean tryAcquire(int arg){if (state == 0) {// 当前 state == 0 的话 是否能直接抢锁  肯定不行// 公平锁是先来后到   条件1:当前锁中阻塞队列没有线程等待 条件2:cas方式去设置state避免线程并发下同时进行state = 1// 抢占成功if(!hasQueuedPredecessor() && casState(0,arg)) {exclusiveOwerThread = Thread.currentThread();   // 设置其独占线程return true;}// 当前锁被占用 并且 自己正在占用线程} else if(Thread.currentThread().equals(exclusiveOwerThread)) {// 锁被重入// 当前不会出现并发,条件限制只可能有一个线程进来int c = getState();c += arg;state = c;} else {}return false;}/*** 判断队列中当前是否有线程等待* true - 有等待* false - 无等待* hasQueuedPredecessor 调用锁 lock -> acquire -> tryAcquire -> hasQueuedPredecessor** 什么时候返回 false* 1. 队列为空* 2. 抢锁的线程是head.next线程的时候 (head.next是唯一在队中拥有枪锁资格的线程)*/private boolean hasQueuedPredecessor() {Node h = head;Node t = tail;Node s;// h != t : 1.head和tail都为空 2. head和tail指向同一个节点// (s = h.next) == null :head和tail指向同一节点  队列在释放head的时候 有一瞬间会处于 head.next为空的状态// s.thread == Thread.currentThread() : 当前线程是不是next节点return h != t &&((s = h.next) == null || s.thread == Thread.currentThread());}@Overridepublic void unlock() {release(1);}/*** 释放锁* @param arg*/private void release(int arg){if(tryRelease(arg)){// 说明完全释放成功了 队列中还有node睡觉中 需要唤醒Node head = getHead();if(head.next != null){unparkSuccessor(head);}}}private void unparkSuccessor(Node node){Node next = node.next;if (next != null && next.thread != null) {LockSupport.unpark(next.thread);}}/*** true - 完全释放锁* false - 部分释放锁* @param arg* @return*/private boolean tryRelease(int arg){int c = getState() - arg;if(getExclusiveOwerThread() != Thread.currentThread()){throw new RuntimeException("fuck error");}// 执行到这里 不会有并发if(c == 0){// 完全释放锁的状态// 要做:// 1. ownerThread = null// 2. state = 0exclusiveOwerThread = null;state = 0;return true;}state = 0;return false;}private final boolean casState(int expect, int update){return true;}private final boolean casHead(Node update){return true;}private final boolean casTail(Node expect, Node update){return true;}
}

相关文章:

ReentranLock手写

ReentranLock手写 整体概述 MiniLock 是一个自定义的锁实现,模拟了 Java ReentrantLock 的公平锁机制。公平锁的核心思想是“先来后到”,即线程按照请求锁的顺序依次获取锁,避免线程饥饿。代码使用了以下关键组件: state: 表示…...

Channel-wise Knowledge Distillation for Dense Prediction论文阅读和

paper:https://arxiv.org/pdf/2011.13256.pdf code:https://github.com/open-mmlab/mmrazor 这篇paper主要是商汤开源的mmrazor中提及在detection有效果,我之前记录的几篇sota文章虽然在各自的paper中在detection领域都有提及有增益&#…...

deepSpeed多机多卡训练服务器之间,和服务器内两个GPU是怎么通信

DeepSpeed 在多机多卡训练时,主要依赖 NCCL 和 PyTorch Distributed 进行通信。具体来说,分为服务器之间和服务器内两种情况: 1. 服务器之间的通信(跨节点通信) DeepSpeed 采用 NCCL(NVIDIA Collective Communications Library)作为主要的通信后端,结合 PyTorch Distr…...

Mysql-经典实战案例(10):如何用PT-Archiver完成大表的自动归档

真实痛点:电商订单表存储优化场景 现状分析 某电商平台订单表(order_info)每月新增500万条记录 主库:高频读写,SSD存储(空间告急)历史库:HDD存储,只读查询 优化目标 …...

centos 7 搭建FTP本地用户

在 CentOS 7 系统上基于本地用户搭建 FTP 服务,可按以下步骤操作: 1. 安装 vsftpd 服务 vsftpd 是一款常用的 FTP 服务器软件,可借助 yum 来安装: bash yum install -y vsftpd2. 启动并设置开机自启 vsftpd 服务 bash systemct…...

HarmonyOS Next~鸿蒙系统功耗优化体系解析:前台交互与后台任务的全场景节能设计

HarmonyOS Next~鸿蒙系统功耗优化体系解析:前台交互与后台任务的全场景节能设计 鸿蒙操作系统(HarmonyOS)凭借其分布式架构与全场景协同能力,在功耗优化领域实现了从用户交互到系统底层的多维度创新。本文从前台用户低…...

混元视频与万相2.1全面对比分析

混元视频与万相2.1全面对比分析(2025版) 一、模型背景与技术定位 混元视频(HunYuan Video) 由腾讯开源,定位为“影视级AI视频生成工具”。核心能力集中在图生视频领域。模型架构基于13B参数规模,强调导演级…...

正则表达式:文本处理的瑞士军刀

正则表达式:文本处理的瑞士军刀 正则表达式(Regular Expression,简称 Regex)是一种用于匹配、查找和操作文本的强大工具。它通过定义一种特殊的字符串模式,可以快速地在文本中搜索、替换或提取符合特定规则的内容。正…...

【负载均衡系列】HAProxy

HAProxy(High Availability Proxy)是一款高性能的 ​TCP/HTTP 负载均衡器,专注于提供高可用性、灵活性和可靠性。以下是关于HAProxy的详细解析,涵盖其工作原理、工作机制、工作模式等核心方面: 一、HAProxy 工作原理 HAProxy的核心职责是将客户端请求高效、可靠地分发到后…...

设计模式之责任链模式:原理、实现与应用

引言 责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象有机会处理请求,从而避免请求的发送者与接收者之间的耦合。责任链模式通过将多个处理对象连接成一条链,使得请求沿着链传递&am…...

20250318在ubuntu20.04中安装向日葵

rootrootrootroot-X99-Turbo:~$ sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb rootrootrootroot-X99-Turbo:~$ sudo apt-get install -f rootrootrootroot-X99-Turbo:~$ sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb 20250318在ubuntu20.04中安装向日葵 2025/3…...

Kotlin的 noinline和crossinline关键字

noinline 顾名思义,noinline的意思就是不内联,这个关键字只能作用于内联高阶函数的某个函数类型的参数上,表明当前的函数参数不参与高阶函数的内联: inline fun fun1(doSomething1: () -> Unit, noinline doSomething2: () -&…...

区块链交易签名相关知识总结

基础概念 签名流程 安全相关问题 实际场景 代码示例 进阶问题 一、基础概念 1. 为什么区块链交易需要签名? 答案: 身份认证:证明交易由私钥持有者发起。 数据完整性:确保交易内容未被篡改。 抗抵赖性:签名者无…...

Spring Boot集成Redis并设置密码后报错: NOAUTH Authentication required

报错信息: io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.Redis密码配置确认无误,但是只要使用Redis存储就报这个异常。很可能是因为配置的spring.redis.password没有被读取到。 基本依赖: implementat…...

如何记录Matlab程序运行过程中所占用的最大内存(续)

在上一篇博客中,我们讨论了如何记录Matlab程序运行过程中所占用的最大内存。 博客原文:如何记录Matlab程序运行过程中所占用的最大内存-CSDN博客 但经过测试发现,这与实际有非常大的差异。运行如下例子: clear;clc; profile on…...

分布式节点池:群联云防护抗DDoS的核心武器

一、节点池的核心作用与架构设计 1. 全球分布式节点布局 物理层防御: 根据产品文档,群联在全球部署“海量分布式节点”,每个节点具备独立清洗能力,攻击流量被分散至不同区域节点处理。优势:避免传统单节点防护的瓶颈&…...

Java线程池深度解析:从使用到调优

适合人群:Java中级开发者 | 并发编程入门者 | 系统调优实践者 目录 一、引言:为什么线程池是Java并发的核心? 二、线程池核心知识点详解 1. 线程池核心参数与原理 2. 线程池的创建与使用 (1) 基础用法示例 (2) 内置线程池的隐患 3. 线…...

自动驾驶背后的数学:多模态传感器融合的简单建模

上一篇博客自动驾驶背后的数学:特征提取中的线性变换与非线性激活 以单个传感器为例,讲解了特征提取中的线性变换与非线性激活。 这一篇将以多模态传感器融合为例,讲解稍复杂的线性变换和非线性激活应用场景。 (一)权重矩阵的张量积分解 y = W x + b = [ w 11 ⋯ w 1 n ⋮…...

12 File文件对象:创建、获取基本信息、遍历文件夹、查找文件;字符集的编解码 (黑马Java视频笔记)

文章目录 File >> 存储数据的方案1. 认识File2. File操作2.1 创建File对象2.2 File操作1)对文件对象的信息的操作2)文件/文件夹的创建/删除3)⭐⭐对文件夹的遍历 3. 方法递归3.1 认识递归3.2 递归算法及其执行流程1) 案例:2…...

HTML应用指南:利用GET请求获取猫眼电影日票房信息——以哪吒2为例

2025年春节档期,国产动画电影《哪吒之魔童闹海》(以下简称《哪吒2》)以颠覆性的叙事风格与工业化制作水准震撼登场,不仅刷新了中国动画电影的票房纪录,更成为全球影史现象级作品。影片凭借春节档期的爆发式开局、持续5…...

荣耀手机卸载应用商店、快应用中心等系统自带的

1.下载abd ADB Download - Get the latest version of ADB and fastboot 2.手机打开开发者选项 3.手机接电脑打开USB调试 4.下载MT管理器查看系统包名 D:\1.LFD\ADB\platform-tools-latest-windows\platform-tools>adb shell adb.exe: no devices/emulators found 这边是…...

[AI速读]用持续集成(CI)优化芯片验证环境:Jenkins与EDA工具的实战指南

在芯片验证中,回归测试(Regression Test)是确保设计稳定性的关键步骤。但随着设计复杂度增加,手动管理海量测试用例、分析日志和覆盖率数据变得异常耗时。本文将介绍如何利用持续集成(CI)工具Jenkins,结合EDA验证环境(如Cadence vManager),实现自动化测试与结果分析,…...

苍穹外卖学习笔记

整体概述 1).用户层 本项目中在构建系统管理后台的前端页面,我们会用到H5、Vue.js、ElementUI、apache echarts(展示图表)等技术。而在构建移动端应用时,我们会使用到微信小程序 2).网关层 Nginx是一个服务器,主要用来作为Http服务器&…...

Spring常用注解汇总

1. IOC容器与Bean管理 注解说明示例Component通用注解,标记类为Spring Bean Component public class MyService { ... } Controller标记Web控制器(应用在MVC的控制层) Controller public class UserController { ... } Service标记业务逻辑层…...

深度强化学习中的深度神经网络优化策略:挑战与解决方案

I. 引言 深度强化学习(Deep Reinforcement Learning,DRL)结合了强化学习(Reinforcement Learning,RL)和深度学习(Deep Learning)的优点,使得智能体能够在复杂的环境中学…...

每日一题力扣2974.最小数字游戏c++

2974. 最小数字游戏 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:vector<int> numberGame(vector<int>& nums) {vector<int> arr(nums.size());sort(nums.begin(),nums.end());for(size_t i0;i<nums.size();i2){arr[i]nums[i1]…...

软考中级-软件设计师 准备

软考中级-软件设计师 准备 一、软考相关1.1、考试时间1.2、考试时长1.3、题型和分值&#xff1a; 二、软考备考2.1、相关书籍2.2、推荐课程&#xff1a;B站up主zst_20012.3、学习路线 一、软考相关 1.1、考试时间 一年有两次软考&#xff0c;一般是五月末和十一月的中旬 以下…...

EasyRTC嵌入式音视频通信SDK:WebRTC技术下的硬件与软件协同演进,开启通信新时代

在当今数字化时代&#xff0c;智能设备的普及和人们对实时通信需求的不断增长&#xff0c;推动了嵌入式音视频通信技术的快速发。EasyRTC嵌入式音视频通信SDK凭借其独特的技术特点和应用优势&#xff0c;在嵌入式设备和多平台实时通信领域脱颖而出。 1、轻量级设计与高性能 Ea…...

lua垃圾回收

lua垃圾回收 lua 垃圾回收 lua 垃圾回收 collectgarbage(“count”)获取当前lua脚本占用内存字节数(单位为KB)。 collectgarbage(“collect”)执行一次垃圾回收。 xxxnil 将变量置为空&#xff0c;会释放内存。 lua中的机制和c#中回收机制很类似 解除羁绊(置为空)。 --垃圾回…...

Lineageos 22.1(Android 15)实现负一屏

一、前言 方案是参考的这位大佬的&#xff0c;大家可以去付费订阅支持一波。我大概理一下Android15的修改。 大佬的方案代码 二、Android15适配调整 1.bp调整&#xff0c;加入aidl引入&#xff0c;这样make之后就可以索引代码了 filegroup {name: "launcher-src"…...