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

详解ReentrantLock--三种加锁方式

目录

介绍AQS:

直观方式解释加锁的流程:

Node是什么:它里面有什么属性呢

图解队列的排队过程:

源码分析三种加锁流程:

我们先讲解一下非公平锁的加锁流程:

Lock()方式加锁:

在源码里对于Lock()的解释:

源码详解枷锁的过程:

进入lock()方法后显示

进入sync.lock();方法:

展示非公平锁的实现lock():

展示公平锁实现的lock()

产看方法acquire(1);

查看方法tryAcquire(int arg):

非公平锁实现:

公平锁实现:

查看创建Node的方法addWaiter(Node.EXCLUSIVE)

查看方法acquireQueued();

查看方法shouldParkAfterFailedAcquire()

注:Node()状态

tryLock()方式加锁:

tryLock()方式:

tryLock(long time, TimeUnit unit)方式

tryAcquireNanos()方法

doAcquireNanos():

 cancelAcquire()

lockInterruptibly()方式

acquireInterruptibly()方法

doAcquireInterruptibly()方法:


在我们打开源码时,可以看一下这个源码的作者:

/** @since 1.5* @author Doug Lea*/

Doug Lea 是 Java 并发编程领域的权威人物,他在并发编程方面的贡献尤为突出。Doug Lea 是 SUNY Oswego 的计算机科学教授,并且是纽约先进技术中心软件工程实验室的联合主任。他的工作对 Java 并发编程产生了深远的影响。

Doug Lea 撰写了《JAVA并发编程实践》一书,这本书是 Java 领域的经典之作,全面深入地探讨了 Java 平台上的多线程和并发编程技术。这本书不仅帮助开发者理解和掌握如何在 Java 环境中编写高效、安全且可维护的并发代码,还提供了许多标准设计技术,用于解决常见的并发编程挑战。

Doug Lea 在 Java 并发编程中的另一个重要贡献是开发了 java.util.concurrent 包,这个包在 JDK 5.0 中被整合,极大地丰富了 Java 的并发编程工具。这个包包括了线程池、并发集合、原子变量和锁等工具,极大地简化了并发编程的复杂性。

介绍AQS:

  • 全程为AbstractQueuedSynchronizer

  • AQS中文被称为队列同步器

  • AQS是一个抽象的接口。他是JUC包下的基类。JUC包下的很多功能都是基于AQS实现的。

  • AQS里面维护了一个由volitile修饰的state变量和内置的FIFO队列完成线程的排队和加锁操作。

  • volitile维护的state保证了可见性和有序性,在设置state是,里面提供了compareAndSetState()方法。CAS方式保证了原子性

直观方式解释加锁的流程:

为大家图解什么是FIFO队列,队列里的数据类型是什么。是如何连接的

  • FIFO队列是双向链表实现的。

  • 里面每个节点的数据类型是Node

  • 特殊的是,队列的头节点是一个伪节点,这个节点的线程信息是null

Node是什么:它里面有什么属性呢

Node是AQS的内部类。

  • 为大家展示Node的结构:

  • 里面记录着状态,前置的指针,后置的指针,还记录了队列的头节点和尾节点
图解队列的排队过程:

这个图片只是为了展现这个队列的状态,对于流程我们并没直接的展现,但是需要注意的是伪节点的Thread是null.

源码分析三种加锁流程:

我们先讲解一下非公平锁的加锁流程:
  1. 如果线程A执行CAS将state修改为1,则线程1得到所资源,执行业务代码。

  2. 这时线程B在去获取所资源是发现state不是0,并且自己并不是有锁的线程,那么B将自己封装为Node,进入双向队列。

  3. 但是在进入队列是,头节点必须是一个伪节点,若一开始没有伪节点,那就创建之后,将这个节点挂在伪节点后,将前置节点的状态修改为-1 之后就可以挂起线程

    我们在创建ReentrantLock()时都是借助多态创建.在此为大家展示一下lock的API

 

Lock()方式加锁:
在源码里对于Lock()的解释:
Acquires the lock.
If the lock is not available then the current thread becomes disabled for thread scheduling purposes and lies dormant until the lock has been acquired.
Implementation Considerations
A Lock implementation may be able to detect erroneous use of the lock, such as an invocation that would cause deadlock, and may throw an (unchecked) exception in such circumstances. The circumstances and the exception type must be documented by that Lock implementation.
获取锁。
如果锁不可用,则当前线程将出于线程调度目的而被禁用,并在获取锁之前处于休眠状态。
实施注意事项
实现 Lock 可能能够检测到对锁的错误使用,例如会导致死锁的调用,并可能在这种情况下引发(未经检查的)异常。该实现必须记录 Lock 情况和异常类型。
源码详解枷锁的过程:
进入lock()方法后显示
public void lock() {sync.lock();}
进入sync.lock();方法:
abstract void lock();//发现是抽象方法。
展示非公平锁的实现lock():
 final void lock() {//首先进入之后直接使用CAS方式修改,如果成功,将持有锁的线程设置为当前线程,返回if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());else  //失败进入acquire方法acquire(1);}
展示公平锁实现的lock()
final void lock() {//发现公平锁下的方式就是  acquire(1);     acquire(1);
}
产看方法acquire(1);
 public final void acquire(int arg) {//尝试获取线程,如果为获取if (!tryAcquire(arg) &&//开始方法acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//addWaiter(Node.EXCLUSIVE)开始封装Node对象  Node.EXCLUSIVE这个指的是现在的锁的类型时互斥锁//封装后讲数据直接开始放置到队列里面acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//将自己终端,就是在队列里面排队,等待唤醒selfInterrupt();}
查看方法tryAcquire(int arg):
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}
非公平锁实现:
protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);
}

进入方法:nonfairTryAcquire(acquires)

final boolean nonfairTryAcquire(int acquires) {//设置线程为当前线程final Thread current = Thread.currentThread();//获取stateint c = getState();//c==0  没有线程持有锁,抢一把if (c == 0) {//CAS方式枪锁成功,设置一下当前线程,直接返回trueif (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}//state不是0,但是线程时但该案当前线程,锁重入 state+1  直接返回获取锁成功else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;//放置state超过int最大值if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");//设置state的值setState(nextc);return true;}//未成功返回false  返回的是flase之后开始将这个线程放入到FIFO队列里return false;
}
公平锁实现:
 protected final boolean tryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();//获取state的值int c = getState();//如果是0的话,这里展现的是公平锁的君子风范:if (c == 0) {//如果里面没有Node或者是头节点之后没有Node等待,或者自己就是头节点之后的点那就可以尝试拿锁if (!hasQueuedPredecessors() &&//如果成功  返回truecompareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}//state不是0,但是线程时但该案当前线程,锁重入 state+1  直接返回获取锁成功else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;//放置state超过int最大值if (nextc < 0)throw new Error("Maximum lock count exceeded");//设置state的值setState(nextc);return true;}//未成功返回false;返回的是flase之后开始将这个线程放入到FIFO队列里return false;}
//就是判断公平锁是否能够干活
public final boolean hasQueuedPredecessors() {// The correctness of this depends on head being initialized// before tail and on head.next being accurate if the current// thread is first in queue.Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());
}
查看创建Node的方法addWaiter(Node.EXCLUSIVE)
/**
*就是简单的创建一个node
*/
private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}enq(node);return node;}
查看方法acquireQueued();
//对于lock()方法里面的中断操作我们先忽略。
final boolean acquireQueued(final Node node, int arg) {//设置状态,当前未获取到锁资源boolean failed = true;try {boolean interrupted = false;//死循环,必须完成一件事 for (;;) {//查找当前节点的前驱节点final Node p = node.predecessor();//开始判断,如果我的前置节点就是伪节点(注意是伪节点)直接在词使用tryAcquire()尝试获取锁资源if (p == head && tryAcquire(arg)) {//如果成功,就设置一些头节点setHead(node);p.next = null; // help GC//设置已经获取锁资源failed = false;//返回的值是false.return interrupted;}//如果前置节点不是伪节点的话,if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}
查看方法shouldParkAfterFailedAcquire()
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;//如果等于-1 证明前置节点可以唤醒当前节点  if (ws == Node.SIGNAL) return true;if (ws > 0) {//如果是>0的话就是当前前置节点无效,使用循环找到有效的节点,地将当前节点挂在有效节点的后面do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {//那就是-2,-3使用ACS将状态设置为-1compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}
注:Node()状态
        /** waitStatus value to indicate thread has cancelled */static final int CANCELLED =  1;/** waitStatus value to indicate successor's thread needs unparking */static final int SIGNAL    = -1;/** waitStatus value to indicate thread is waiting on condition */static final int CONDITION = -2;/*** waitStatus value to indicate the next acquireShared should* unconditionally propagate*/static final int PROPAGATE = -3;
tryLock()方式加锁:
tryLock()方式:
public boolean tryLock() {//这就是非公平锁的TryAcquire()方法return sync.nonfairTryAcquire(1);
}
final boolean nonfairTryAcquire(int acquires) {//设置线程为当前线程final Thread current = Thread.currentThread();//获取stateint c = getState();//c==0  没有线程持有锁,抢一把if (c == 0) {//CAS方式枪锁成功,设置一下当前线程,直接返回trueif (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}//state不是0,但是线程时但该案当前线程,锁重入 state+1  直接返回获取锁成功else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;//放置state超过int最大值if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");//设置state的值setState(nextc);return true;}//未成功返回false  返回的是flase之后开始将这个线程放入到FIFO队列里return false;
}

tryLock(long time, TimeUnit unit)方式
public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
tryAcquireNanos()方法
 public final boolean tryAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {//如果线程中断,抛出异常if (Thread.interrupted())throw new InterruptedException();return tryAcquire(arg) //在抢一手,失败的话执行下面的方法||doAcquireNanos(arg, nanosTimeout);}
doAcquireNanos():
private boolean doAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {//如果时间结束,直接返回失败if (nanosTimeout <= 0L)return false;//设置结束时间final long deadline = System.nanoTime() + nanosTimeout;//将当前节点设置出来final Node node = addWaiter(Node.EXCLUSIVE);//设置时候失败获取锁boolean failed = true;try {for (;;) {  //这是死循环,我们注意查看代码的出口//获取当前节点的前置节点final Node p = node.predecessor();//如果前置节点是伪节点,尝试拿锁if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;//成功直接返回return true;}//计算现在还剩多长时间nanosTimeout = deadline - System.nanoTime();//时间结束,直接返回falseif (nanosTimeout <= 0L)return false;//时间还是有的,修改前置节点的状态,准备挂起if (shouldParkAfterFailedAcquire(p, node) &&//时间有,但是太少了,连挂起的时间都不足,也是结束nanosTimeout > spinForTimeoutThreshold)//如果时间足够,开始将节点挂起 继续死循环等待结束条件LockSupport.parkNanos(this, nanosTimeout);//如果当前线程被其他线程中断,直接甩出异常if (Thread.interrupted())throw new InterruptedException();}} finally {//甩出异常也是结束死循环,但是需要将节点取消if (failed)cancelAcquire(node);}}
 cancelAcquire()

private void cancelAcquire(Node node) {//如果是null  直接返回if (node == null)return;//将当前节点的线程设置为nullnode.thread = null;//找到前置线程Node pred = node.prev;//找到状态正常的前置节点while (pred.waitStatus > 0)node.prev = pred = pred.prev;//  将前置线程的next设置为当前线程的nextNode predNext = pred.next;// 设置状态为1,代表节点取消node.waitStatus = Node.CANCELLED;//  如果是尾节点的话,直接就是简单的将前置节点设知为尾节点if (node == tail && compareAndSetTail(node, pred)) {//设置前置节点的next为空compareAndSetNext(pred, predNext, null);} else {//当前的节点不是尾节点或者是CAS操作失败//那就是;两种情况,一种是前置节点就是伪节点  第二种是前置接点不是伪节点int ws;if (pred != head //前置节点不是伪节点&&((ws = pred.waitStatus) == Node.SIGNAL  //前置节点状态是否为-1  ||  //不是-1的话,或着是其他的<0的状态,设置为-1  这样的话可以将线程唤醒(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&//并且前置的线程不是nullpred.thread != null) {Node next = node.next;//判断当前线程有后继节点,将后继节点关在前置节点上if (next != null && next.waitStatus <= 0)compareAndSetNext(pred, predNext, next);} else {//当前节点的前置节点是伪节点  直接将线程唤醒  这是释放锁的操作,在后续的章节开始讲解unparkSuccessor(node);}node.next = node; // help GC}
}

 

lockInterruptibly()方式
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);
}
acquireInterruptibly()方法
public final void acquireInterruptibly(int arg)throws InterruptedException {//判断线程是否终端if (Thread.interrupted())throw new InterruptedException();//如果尝试获取所资源失败if (!tryAcquire(arg))//进入方法doAcquireInterruptibly(arg);
}
doAcquireInterruptibly()方法:
private void doAcquireInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.EXCLUSIVE);//当前获取资源失败boolean failed = true;try {for (;;) //注意出口条件//获取当前节点的前置节点final Node p = node.predecessor();//如果前置节点是伪节点,尝试拿锁if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;//成功直接返回return true;}//获取锁资源失败  开始挂起资源,如果挂起之后被中断的方式挂起,抛出异常if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} finally {//因为异常也是出口条件,将节点取消if (failed)cancelAcquire(node);//还是取消节点的方法,同上}
}  

相关文章:

详解ReentrantLock--三种加锁方式

目录 介绍AQS: 直观方式解释加锁的流程&#xff1a; Node是什么&#xff1a;它里面有什么属性呢 图解队列的排队过程&#xff1a; 源码分析三种加锁流程&#xff1a; 我们先讲解一下非公平锁的加锁流程&#xff1a; Lock()方式加锁&#xff1a; 在源码里对于Lock()的解…...

SQL 基础语法(一)

文章目录 1. SQL 分类2. 数据库操作3. 数据表操作4. 增删改操作5. 查询操作6. 用户管理7. 权限控制 1. SQL 分类 2. 数据库操作 #创建数据库 create database if not exists test;#查询所有数据库 show databases;#查询当前数据库 select database();#删除数据库 drop databas…...

Python酷库之旅-第三方库Pandas(190)

目录 一、用法精讲 881、pandas.Index.is_方法 881-1、语法 881-2、参数 881-3、功能 881-4、返回值 881-5、说明 881-6、用法 881-6-1、数据准备 881-6-2、代码示例 881-6-3、结果输出 882、pandas.Index.min方法 882-1、语法 882-2、参数 882-3、功能 882-4、…...

Spring学习笔记_19——@PostConstruct @PreDestroy

PostConstruct && PreDestroy 1. 介绍 PostConstruct注解与PreDestroy注解都是JSR250规范中提供的注解。 PostConstruct注解标注的方法可以在创建Bean后在为属性赋值后&#xff0c;初始化Bean之前执行。 PreDestroy注解标注的方法可以在Bean销毁之前执行。 2. 依赖…...

《云计算网络技术与应用》实训8-1:OpenvSwitch简单配置练习

1.按《云计算网络技术与应用》实训5-1进行环境配置&#xff0c;安装好OVS 2.开启OVS虚拟交换机 3.创建一个网桥br0 4.查看网桥列表 5.把ens34网卡连接到网桥br0上 6. 查看网桥br0所有端口 7.列出网卡ens34连接的所有网桥列表 8.查看OVS网络状态 9.将网桥br0上连接的网卡ens34删…...

【架构艺术】服务架构稳定性的基础保障

一个产品随着不断研发&#xff0c;其服务架构的复杂度会越来越高。随着产品的用户体量变大&#xff0c;为了保证产品能够长线运营&#xff0c;就需要保证整个服务架构的稳定性。因此&#xff0c;今天这篇文章&#xff0c;就从实操的角度&#xff0c;粗浅讨论一下&#xff0c;服…...

Python中使用pip换源的详细指南

在Python开发过程中&#xff0c;我们经常需要安装各种第三方库。pip是Python的包管理工具&#xff0c;用于安装和管理Python库。然而&#xff0c;由于网络原因&#xff0c;有时访问默认的Python包索引&#xff08;PyPI&#xff09;可能会比较慢。这时&#xff0c;我们可以通过更…...

一站打包国际智慧教育自主学练软件资源

&#x1f451;&#x1f31f;一站打包国际智慧教育自主学练软件与资源平台&#xff0c;欧美学校正在使用&#xff0c;不出国就可以学&#x1f452;&#x1f388; &#x1f49b; 多元学练&#xff1a;我们正在使用的自主学练软件是美国学校一线教师使用的&#xff0c;涵盖了英语…...

用股票API获取高频行情数据来实现数据分析和量化

用股市API获取高频行情来实现数据分析和量化 使用股市API是一种有效的方式来获取高频行情数据&#xff0c;以便进行行情数据分析和量化交易。Python是一种广泛应用于金融数据领域的编程语言&#xff0c;它提供了丰富的库和工具&#xff0c;可用于与股市API进行交互。通过调用股…...

C++ | Leetcode C++题解之第526题优美的排列

题目&#xff1a; 题解&#xff1a; class Solution { public:int countArrangement(int n) {vector<int> f(1 << n);f[0] 1;for (int mask 1; mask < (1 << n); mask) {int num __builtin_popcount(mask);for (int i 0; i < n; i) {if (mask &am…...

【RabbitMQ】01-RabbitMQ

1. MQ MQ可以有更好的并发性。 2. 安装 docker run \-e RABBITMQ_DEFAULT_USERitheima \-e RABBITMQ_DEFAULT_PASS123321 \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \--network hm-net\-d \rabbitmq:3.8-management3. 结构 4. 数据…...

使用 ADB 在某个特定时间点点击 Android 设备上的某个按钮

前提条件 安装 ADB&#xff1a;确保你已经在计算机上安装了 Android SDK&#xff08;或单独的 ADB&#xff09;。并将其添加到系统环境变量中&#xff0c;以便你可以在命令行中运行 adb。 USB调试&#xff1a;确保 Android 设备已启用 USB 调试模式。这可以在设备的“设置” -…...

【随笔】对于开发者而言,你对什么事情感到失落?亦或者你上一次感到有成就感是什么时候?你遇到过怎样格局的老板?

这是博主的一篇随笔文章&#xff0c;一起和大家聊聊工作上的一些事和一些感受&#xff0c;我觉得我们这个群体&#xff0c;同样有很多优秀的、幽默的人。只不过有些表达和沟通并不是我们擅长的&#xff0c;包括博主也是&#xff0c;这是我们的劣势和缺点。没关系&#xff0c;这…...

【LeetCode】两数之和返回两数下标、数组形式整数相加

主页&#xff1a;HABUO&#x1f341;主页&#xff1a;HABUO 1.两数之和返回两数下标 题目&#xff1a;给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输…...

Kubernetes中的secrets存储

华子目录 2.secrets2.1secrets功能介绍2.2secrets的创建2.2.1从文件创建2.2.2编写yaml文件 2.3secret的使用案例2.3.1将secret挂载到volume中2.3.2设置子目录映射secret密钥2.3.3将secret设置为环境变量2.3.4存储docker register的认证信息spec.imagePullSecrets[] 2.secrets …...

使用 Elastic、OpenLLMetry 和 OpenTelemetry 跟踪 LangChain 应用程序

作者&#xff1a;来自 Elastic Bahubali Shetti Langchain 应用程序的使用正在增长。构建基于 RAG 的应用程序、简单的 AI 助手等的能力正在成为常态。观察这些应用程序更加困难。考虑到现有的各种选项&#xff0c;本博客展示了如何将 OpenTelemetry 检测与 OpenLLMetry 结合使…...

【论文复现】VALL-E:语音合成的新里程

&#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐、摄影的一位博主。 &#x1f4d7;本文收录于论文复现系列&#xff0c;大家有兴趣的可以看一看。 &#x1f4d8;相关专栏C语言初阶、…...

java项目之微服务在线教育系统设计与实现(springcloud)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的闲一品交易平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 微服务在线教育系统设计与…...

P3-1.【结构化程序设计】第一节——知识要点:算法、顺序结构程序设计、if语句的语法结构及各种用法

讲解视频&#xff1a; P3-1.【结构化程序设计】第一节——知识要点&#xff1a;算法、顺序结构程序设计、if语句的语法结构及各种用法 知识要点&#xff1a;算法、顺序结构程序设计、if语句的语法结构及各种用法 一、算法、顺序结构程序设计任务分析 知识要点&#xff1a;算法…...

Vue2——单页应用程序路由的使用

一.单页应用程序与多页应用程序之间的比较 二.单页的应用场景 系统类网站 / 内部网站 / 文档类网站 / 移动端网站 三.路由的介绍 1. 什么是路由 路由是一种映射关系 2. Vue中的路由是什么 路径和组件的映射关系 四.VueRouter的使用 5个基础步骤&#xff08;固定&#xff09; …...

如何通过技术优化提升Element Plus开发效率

如何通过技术优化提升Element Plus开发效率 【免费下载链接】element-plus &#x1f389; A Vue.js 3 UI Library made by Element team 项目地址: https://gitcode.com/GitHub_Trending/el/element-plus 在前端开发过程中&#xff0c;Element Plus作为一款基于Vue.js 3…...

构建智能体的专业技能树 - Agent Skills生态全析(中篇)

一、概述 这篇文章我们将围绕Skills、Tools、MCP、Subagents 四个组件有什么区别、Anthropic 官方做好了哪些现成 Skills、如何从零创建一个自定义 Skill 的完整流程 这些四个方面来进行讲解。 二、智能体生态系统概览 在 Anthropic 构建的智能体生态中&#xff0c;多种技术组件…...

实战指南:基于快马平台生成Spring Boot电商后端并部署于腾讯云龙虾

最近在做一个电商平台的后端开发项目&#xff0c;需要快速搭建一套完整的API服务。考虑到腾讯云龙虾服务器性价比高&#xff0c;特别适合中小型Web应用部署&#xff0c;我决定用Spring Boot框架来实现。整个过程在InsCode(快马)平台上完成&#xff0c;从代码生成到部署上线一气…...

QuickSnap:Blender三维建模效率革命,快速对齐插件让精准建模变得简单

QuickSnap&#xff1a;Blender三维建模效率革命&#xff0c;快速对齐插件让精准建模变得简单 【免费下载链接】quicksnap Blender addon to quickly snap objects/vertices/points to object origins/vertices/points 项目地址: https://gitcode.com/gh_mirrors/qu/quicksnap…...

别再手动改稿了!用LaTeX的soul包搞定论文批注(删除线/高亮/引用兼容)

LaTeX高效批注指南&#xff1a;用soul包实现学术协作的优雅排版 当导师的红色批注铺满论文初稿&#xff0c;或是合作者发来二十处修改意见时&#xff0c;大多数研究者都会面临一个共同困境——如何在保留原始内容的同时清晰标记修改痕迹&#xff1f;传统的手动添加删除线或高亮…...

从NDVI到地表温度:用ENVI Band Math一次性搞定植被与热环境分析

ENVI波段运算实战&#xff1a;NDVI与地表温度的高效批量处理技巧 遥感影像分析中&#xff0c;植被指数和地表温度是最基础却又最关键的指标。传统操作流程往往需要反复切换不同工具模块&#xff0c;既耗时又容易出错。而ENVI的Band Math功能就像一把瑞士军刀&#xff0c;能将这…...

Z-Image-GGUF中文支持实测:古风建筑、水墨山水、国潮设计等本土化效果展示

Z-Image-GGUF中文支持实测&#xff1a;古风建筑、水墨山水、国潮设计等本土化效果展示 1. 引言&#xff1a;当AI绘画遇上东方美学 最近在测试各种文生图模型时&#xff0c;我发现了一个挺有意思的现象&#xff1a;很多国外开发的AI绘画工具&#xff0c;在处理中国传统文化元素…...

DeepSeek-R1-Distill-Qwen-7B优化升级:提升推理速度的技巧

DeepSeek-R1-Distill-Qwen-7B优化升级&#xff1a;提升推理速度的技巧 1. 模型概述 DeepSeek-R1-Distill-Qwen-7B是基于Qwen架构的7B参数蒸馏模型&#xff0c;由DeepSeek团队开发。该模型通过知识蒸馏技术从更大的DeepSeek-R1模型中提取关键知识&#xff0c;在保持较高推理能…...

douyin-downloader:3大核心能力破解抖音内容高效下载难题

douyin-downloader&#xff1a;3大核心能力破解抖音内容高效下载难题 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback su…...

RTX 3090环境下的BEVFusion实战部署:从源码编译到多模态训练调优

1. RTX 3090环境准备与BEVFusion适配 在RTX 3090上部署BEVFusion最大的挑战就是硬件与软件版本的兼容性问题。官方推荐的环境是CUDA 9.2和PyTorch 1.3.1&#xff0c;但这对于RTX 3090来说完全不适用——30系显卡需要CUDA 11才能发挥全部性能。我刚开始尝试直接按照官方文档安装…...