并发编程原理与实战(二十四)Java并发基石LockSupport park/unpark机制全解析

Java中的线程休眠大法系列(三)LockSupport.park() 文章目录前言一、看看JDK的代码注释二、案例1. 查看线程状态2.线程被打断,不抛异常2.是否会释放锁3.传递自定义数据总结 前言 Java的线程休眠我们从Thread.sleep到Object.wait(),都进行了逐一讲解,现在我们研究最后一种方式LockSupport.park()。 一、看看JDK的代码注释 /** * Disables the current thread for thread scheduling purposes unless the * permit 阅读详情

前面的文章我们已经学习了synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock等多种锁,这些锁都是多线程并发协同的重要工具,本文来学习另一个提供了一种低开销、高灵活的多线程控制方式的工具LockSupport,Java并发工具链的底层基石之一‌。

什么是LockSupport

/**
 * Basic thread blocking primitives for creating locks and other
 * synchronization classes.
 *
 * <p>This class associates, with each thread that uses it, a permit
 * (in the sense of the {@link java.util.concurrent.Semaphore
 * Semaphore} class). A call to {@code park} will return immediately
 * if the permit is available, consuming it in the process; otherwise
 * it <em>may</em> block.  A call to {@code unpark} makes the permit
 * available, if it was not already available. (Unlike with Semaphores
 * though, permits do not accumulate. There is at most one.)
 * Reliable usage requires the use of volatile (or atomic) variables
 * to control when to park or unpark.  Orderings of calls to these
 * methods are maintained with respect to volatile variable accesses,
 * but not necessarily non-volatile variable accesses.
 */

LockSupport用于创建锁及其他同步类的基础线程阻塞原语。

本类为每个调用线程关联一个许可(概念类似于 java.util.concurrent.Semaphore信号量)。调用park方法时,若许可可用则立即返回并消耗该许可;否则线程可能阻塞直到许可可用(park的意思是把…搁置,就是让线程搁置的意思)。调用unpark则使许可变为可用状态(若尚未可用),即释放许可。与信号量不同,许可不可累积,每个线程最多持有一个。可靠使用需配合volatile或原子变量控制park/unpark调用时序。这些方法的调用顺序与volatile变量访问具有内存可见性保证,但与非volatile变量访问无必然关联。

LockSupport实现的线程阻塞/唤醒机制

 /* <p>Methods {@code park} and {@code unpark} provide efficient
 * means of blocking and unblocking threads that do not encounter the
 * problems that cause the deprecated methods {@code Thread.suspend}
 * and {@code Thread.resume} to be unusable for such purposes: Races
 * between one thread invoking {@code park} and another thread trying
 * to {@code unpark} it will preserve liveness, due to the
 * permit. Additionally, {@code park} will return if the caller's
 * thread was interrupted, and timeout versions are supported. The
 * {@code park} method may also return at any other time, for "no
 * reason", so in general must be invoked within a loop that rechecks
 * conditions upon return. In this sense {@code park} serves as an
 * optimization of a "busy wait" that does not waste as much time
 * spinning, but must be paired with an {@code unpark} to be
 * effective.
 */

park和unpark方法提供了高效的线程阻塞/唤醒机制,避免了弃用方法 Thread.suspend和Thread.resume的固有缺陷:由于许可机制的存在,当线程A调用park与线程B尝试unpark A时,不会出现死锁问题。此外,若调用线程被中断,park会立即返回,同时支持超时版本。需注意park可能因"未知原因"虚假唤醒,因此通常应在循环中调用并重新检查条件。在此意义上,park是对"忙等待"的优化,减少CPU空转,但必须配合unpark使用才能生效。

 /* <p>The three forms of {@code park} each also support a
 * {@code blocker} object parameter. This object is recorded while
 * the thread is blocked to permit monitoring and diagnostic tools to
 * identify the reasons that threads are blocked. (Such tools may
 * access blockers using method {@link #getBlocker(Thread)}.)
 * The use of these forms rather than the original forms without this
 * parameter is strongly encouraged. The normal argument to supply as
 * a {@code blocker} within a lock implementation is {@code this}.
 */

park方法的三种重载形式均支持blocker对象参数。该对象会在线程阻塞时被记录,便于监控诊断工具识别线程阻塞原因(工具可通过getBlocker(Thread)获取 blocker 对象)。强烈建议使用带blocker参数的版本。在锁实现中,通常传入this作为blocker参数。

 /* <p>These methods are designed to be used as tools for creating
 * higher-level synchronization utilities, and are not in themselves
 * useful for most concurrency control applications.  The {@code park}
 * method is designed for use only in constructions of the form:
 */

这些方法旨在作为构建高级同步工具的基础设施,其本身并不适用于大多数常规并发控制场景。park方法仅推荐以下列形式使用:

//循环检查条件
while (!canProceed()) {   
    // 确保unpark请求对其他线程可见   ...   
    LockSupport.park(this); 
} 

从上面的建议的写法来看,为了防止伪唤醒的问题,必须使用while循环不断检查条件。

 /* where no actions by the thread publishing a request to unpark,
 * prior to the call to {@code park}, entail locking or blocking.
 * Because only one permit is associated with each thread, any
 * intermediary uses of {@code park}, including implicitly via class
 * loading, could lead to an unresponsive thread (a "lost unpark").
 */

调用park前,线程发布unpark请求的操作不得包含加锁或阻塞行为。由于每个线程仅关联一个许可,任何中间步骤(包括类加载隐式触发的park)均可能导致线程无响应(即“许可丢失”问题)。

基于LockSupport实现的锁

示例:先进先出非可重入锁

 class FIFOMutex {
   private final AtomicBoolean locked = new AtomicBoolean(false);
   //等待队列  
   private final Queue<Thread> waiters = new ConcurrentLinkedQueue<>();
   //上锁方法
   public void lock() {
     boolean wasInterrupted = false;
     // 当前线程加入等待队列
     waiters.add(Thread.currentThread());

     // 循环检查:当前线程非队首线程或CAS抢锁失败时阻塞
     while (waiters.peek() != Thread.currentThread() ||
            !locked.compareAndSet(false, true)) {
       LockSupport.park(this);
       // 等待过程忽略中断请求,记录中断状态
       if (Thread.interrupted())
         wasInterrupted = true;
     }
	 //抢锁成功移出等待队列
     waiters.remove();
     // 恢复中断状态
     if (wasInterrupted)
       Thread.currentThread().interrupt();
   }

   //释放锁方法  
   public void unlock() {
     //释放锁状态  
     locked.set(false);
     //唤醒队首等待线程  
     LockSupport.unpark(waiters.peek());
   }

   static {
     // 预加载LockSupport类
     Class<?> ensureLoaded = LockSupport.class;
   }
 }

这段代码使用LockSupport+AtomicBoolean+ConcurrentLinkedQueue实现了一个‌轻量级、公平的互斥锁。

  • ‌公平性‌:通过队列保证线程按申请顺序获取锁(对比synchronized的非公平性)。
  • ‌低竞争‌:基于LockSupport,避免synchronized的锁膨胀问题(即随着竞争加剧从低级别锁逐步升级为高级别锁的过程)。
  • ‌轻量级‌:仅依赖AtomicBoolean和并发队列ConcurrentLinkedQueue实现。

(1)lock()方法关键逻辑‌:

‌队列控制‌:只有队首线程有资格尝试获取锁(waiters.peek() == currentThread)。

‌CAS抢锁‌:compareAndSet(false, true)保证只有一个线程能修改锁状态,能将锁状态修改为true即抢锁成功。

中断处理‌:阻塞期间忽略中断,但最终恢复中断标记(避免信号丢失)。

(2)unlock()方法关键逻辑‌:

唤醒机制‌:直接通知队列中的下一个等待线程,避免无效竞争(精准唤醒某个线程,对比notifyAll把全部等待状的线程都唤醒造成"惊群效应")。

(3)静态初始化块设计

预加载LockSupport类,目的‌是防止类加载延迟导致unpark丢失。unpark丢失是指在多线程编程中,唤醒操作(unpark)未能生效,导致后续的等待操作(park)出现不必要的阻塞现象‌。首次调用LockSupport时,JVM需加载原生类库。若此时线程A调用unpark(threadB)释放一个许可信号,而LockSupport类尚未加载完成,导致信号未被记录‌,后续threadB.park()获取不到信号将永久阻塞。

LockSupport API详解

LockSupport 提供的api不多,主要分为两类,一类是阻塞类方法,一类是唤醒类方法。

阻塞类

1、park()

/**
 * Disables the current thread for thread scheduling purposes unless the
 * permit is available.
 *
 * <p>If the permit is available then it is consumed and the call
 * returns immediately; otherwise the current thread becomes disabled
 * for thread scheduling purposes and lies dormant until one of three
 * things happens:
 *
 * <ul>
 *
 * <li>Some other thread invokes {@link #unpark unpark} with the
 * current thread as the target; or
 *
 * <li>Some other thread {@linkplain Thread#interrupt interrupts}
 * the current thread; or
 *
 * <li>The call spuriously (that is, for no reason) returns.
 * </ul>
 *
 * <p>This method does <em>not</em> report which of these caused the
 * method to return. Callers should re-check the conditions which caused
 * the thread to park in the first place. Callers may also determine,
 * for example, the interrupt status of the thread upon return.
 */
public static void park() {
    if (Thread.currentThread().isVirtual()) {
        VirtualThreads.park();
    } else {
        U.park(false, 0L);
    }
}

这是一个静态的方法,调用该方法,若许可可用则立即消耗该许可并返回;否则当前线程将被禁用线程调度,进入休眠状态直至以下三种情况之一发生:

(1)其他线程调用unpark并以当前线程为目标;

(2)其他线程interrupt 中断当前线程;

(3)调用虚假返回(即无明确原因返回)。

2、park(Object blocker)

park(Object blocker),带阻塞对象标识的park(),便于诊断。blocker 是导致当前线程停驻的同步对象,用于诊断线程阻塞原因的同步对象标识,主要作用是为监控工具提供上下文信息。Java官方推荐使用带blocker的park方法替代无参版本,尤其在锁实现中通常传入this作为阻塞标识。如:

void lock() {
    Thread current = Thread.currentThread();
    while (!owner.compareAndSet(null, current)) {
        // 阻塞时记录当前锁实例为blocker
        LockSupport.park(this);  // this作为blocker
    }
}

当线程因竞争锁失败被park时,this会被记录为阻塞源,blocker引用会在park结束后自动清除(通过setBlocker(t, null)),避免内存泄漏。

3、parkUntil(Object blocker, long deadline)

阻塞到绝对时间戳(毫秒)唤醒。

4、parkNanos(long nanos)

阻塞指定纳秒时间后自动唤醒。

唤醒类

unpark(Thread thread)

/**
 * Makes available the permit for the given thread, if it
 * was not already available.  If the thread was blocked on
 * {@code park} then it will unblock.  Otherwise, its next call
 * to {@code park} is guaranteed not to block. This operation
 * is not guaranteed to have any effect at all if the given
 * thread has not been started.
 *
 * @param thread the thread to unpark, or {@code null}, in which case
 *        this operation has no effect
 */
public static void unpark(Thread thread) {
    if (thread != null) {
        if (thread.isVirtual()) {
            VirtualThreads.unpark(thread);
        } else {
            U.unpark(thread);
        }
    }
}

调用该方法为指定线程提供许可(若该许可尚未可用)。若线程正因调用park而阻塞,则立即解除阻塞;否则保证该线程下一次调用park时不会阻塞(因为已经有许可了)。若指定线程尚未启动,则此操作不保证会产生任何效果。

【Java开发日记】我们来说说 LockSupport 的 park 和 unpark LockSupport 和 CAS 是Java并发包中很多并发工具控制机制的基础,它们底层其实都是依赖Unsafe实现。很多锁的类都是基于LockSupport的park和unpark来实现的,所以了解LockSupport类是非常重要的。如果小假的内容对你有帮助,请评论。创作不易,大家的支持就是我坚持下去的动力! 阅读详情

相关推荐

LockSupport原理解析及源码

目录概览LockSupport解析用法:具体使用:原理概述:源码解析:底层原理:park原理流程图:unpark原理流程图:park() 源码:unpark() 源码:Q & A1. unpark是否可以调用多次,以获取多个permit?2. unpark是否一定要在park之后调用?3. park会像wait()一样睡眠释放当前锁吗? LockSupport解析 LockSupport 底层使用Posix线程库pthreads的系统级别锁互斥量mutex和condition,所以它的系统消耗是比

u012464024的博客 903

LockSupport.park 和LockSupport.unpark基本使用和原理

基本使用 // 暂停当前线程 LockSupport.park(); // 恢复某个线程的运行 LockSupport.unpark(暂停线程对象) 特点 与 Object 的 wait & notify 相比 wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必 park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等

weixin_43939924的博客 3304

B站DRM视频解密流程实战解析:从密钥获取到播放全流程

本文详细解析了B站DRM视频的解密全流程,从识别加密视频、捕获关键网络请求(如playurl接口的widevine_pssh数据),到逆向分析DRM SDK的JavaScript执行逻辑,最终手动模拟EME流程获取解密密钥,并使用mp4decrypt和ffmpeg工具完成视频文件的解密与合并。文章为开发者提供了从密钥获取到播放的完整实战指南。

weixin_29290379的博客 838

Java并发编程——LockSupport的park和unpark

熟悉 Java 并发包的人一定对 LockSupport 的 park/unpark 方法不会感到陌生,它是 Lock(AQS)的基石,给 Lock(AQS)提供了挂起/恢复当前线程的能力。

小飞侠的博客 1558

CloudCompare

分割点云:选中要分割的点云文件–>Segment–>左键框出要分割的点或者区域–>Segment Out–>Comfirm Segmentation,原点云文件被分割成两个结构,选中分割出来的remaining,右键delete即可。qSRA (Surface of Revolution Analysis):计算一个点云和一个假定旋转平面之间的距离(旋转平面用2D轮廓定义),距离计算好后,用户可以创建一个偏差的2D图或者圆柱或圆锥的投影。②表现为同样的对象或者至少有同样的形状。

xiaoyaolangwj的博客 4709

VirtualBox增强功能安装全指南:从下载到配置复制粘贴功能

本文详细介绍了VirtualBox增强功能的完整安装与配置流程,重点解决了虚拟机与主机间复制粘贴、文件拖放等核心协作难题。通过分步指南和故障排查,帮助用户在不同操作系统(如Windows、Linux)中实现无缝集成,提升虚拟化工作效率与体验。

weixin_29206669的博客 418

JUC---LockSupport

1. LockSupport原理 LockSupport是一个编程工具类,主要是为了阻塞和唤醒线程用的。 LockSiupport的核心方法有两个,park和unpark,park用来阻塞当前调用线程,unpark用来唤醒指定线程。 LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。 初始时,permit为0,当调用unpark()方法时,线程的per

蓝天⊙白云的博客 350

AC360变频器使用案例

AC360变频器使用案例!

QING_BOSS的博客 517

Java并发基础(10):LockSupport

1、LockSupport简介 LockSupport位于java.util.concurrent.locks包下。LockSupprot是线程的阻塞原语,用来阻塞线程和唤醒线程。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在线程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。 LockSupport 类每个使用它的线程...

mingyuli的博客 759

英飞凌TC377的Flash管理、UCB配置与HSM安全机制解析

本文深入解析了英飞凌TC377微控制器的Flash存储结构、UCB配置机制与HSM安全模块。详细阐述了带Cache与不带Cache的PFlash地址访问差异、利用SOTA与AB Swap实现无感OTA更新的原理,以及如何通过UCB安全配置启动流程与写保护。重点剖析了HSM硬件安全模块在密钥管理、安全启动与安全OTA中的核心作用,为开发高可靠性嵌入式系统提供实战指南。

weixin_29170327的博客 523

【芯片驱动】2. CMT2300A配合硬件测试(灵敏度和发射功率)的软件实现

前言 在开发一款无线射频产品的时候,软件是一部分,硬件也是一部分。而决定无线收发性能的,首先是硬件的匹配电路,然后才到软件部分的优化。 一款无线射频产品,首先需要先决定是在那个频率范围内(当然是国家允许的范围内),然后硬件则需要在基于这个频点范围进行硬件的参数匹配,具体又分为发送功率匹配、天线匹配、和接收灵敏度的匹配。 现在我们使用2300a这款无线芯片来说,除非自己的团队是射频专家,则可以自己进行无线外围电路的设计外,另外一种更加快捷的方式,就是寻找原厂技术支持,他们会给...

sishuihuahua的博客 7052

LockSupport中的park与unpark原理

LockSupport是用来创建locks的基本线程阻塞基元,比如AQS中实现线程挂起的方法,就是park,对应唤醒就是unpark。JDK中有使用的如下 LockSupport提供的是一个许可,如果存在许可,线程在调用park的时候,会立马返回,此时许可也会被消费掉,如果没有许可,则会阻塞。调用unpark的时候,如果许可本身不可用,则会使得许可可用 许可只有一个,不可累加 park源码跟...

爬蜥 3万+

71、机器人与机器人控制器技术解析

本文深入解析了机器人控制器的通信接口、传感器数据采集与处理以及抓手技术三大核心技术。详细介绍了数字I/O、串行接口、总线系统和以太网等通信方式,探讨了各类传感器在焊接、装配、质量检测等场景中的应用,并阐述了手眼校准与路径校正的关键方法。同时,文章还分析了不同类型抓手的特点及选型设计原则,展示了机器人在现代工业自动化中的高效性与灵活性。通过实际编程示例与流程图,全面呈现了机器人系统的集成与控制逻辑。

gin88的博客 275

Java LockSupport以及park、unpark方法源码深度解析

介绍了JUC中的LockSupport阻塞工具以及park、unpark方法的底层原理,从Java层面深入至JVM层面。

终生学习践行者 5762

am62x A53与R5F通信

AM62x是Ti的一款异构多核芯片。remoteproc驱动,硬编码(写死)了R5F/M4F固件的名字,这样驱动启动时,就会找硬件编码的固件,下面是其对应关系。值得注意的是,linux内核中的M4F remoteproc驱动启动M4F,而linux内核中的R5F remoreteproc驱动不启动R5F,相反,它attach到已经在boot阶段启动的R5F固件上。在/lib/firmware的真实固件名:,而我们生成的固件名如下所示,没有直接对应的,经确认,.out文件就是我们需要的,重命名后即可。

鹰之翔 1358

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

LockSupport 与线程通信机制:从 park/unpark 到底层实现

在并发编程中,线程通信的核心就是“挂起与唤醒”。都必须在锁内使用,不够灵活。则是 JUC 的底层工具类,提供更底层、线程级别的挂起与唤醒机制,AQS 的核心原语FutureTask、ReentrantLock、Condition 等的基础实现是所有高层同步工具的“线程调度引擎”。问题答案要点Q1:LockSupport 的作用?提供底层线程挂起与唤醒机制,支撑 AQS、FutureTask 等组件。Q2:park/unpark 与 wait/notify 区别?

dividividiv的博客 945

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

JUC并发编程:LockSupport.park() 与 unpark() 深度解析

LockSupport是Java并发编程中的核心工具类,用于线程的阻塞(park)和唤醒(unpark)。其核心机制是基于"许可"(Permit)的二元信号量,不同于Object.wait/notify必须获取锁且严格顺序执行,LockSupport无需锁支持且允许先unpark后park。底层通过JVM的Parker对象实现,使用互斥锁和条件变量进行线程同步。关键特性包括:不释放持有锁、中断响应不抛异常、许可不叠加等。相比wait/notify,LockSupport更灵活轻量,是AQ

myGodxx的博客 792

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

LockSupport 源码深度解析:Java 并发编程的底层基石

在Java并发编程中,是一个极其重要的工具类,它提供了线程阻塞和唤醒的基础能力。作为Semaphore等并发工具类的底层支撑,是Java并发框架的基石之一。本文将深入解析的源码实现,揭示其背后的原理和设计思想,帮助你更好地理解和应用Java并发编程。作为Java并发编程的底层基石,其设计简洁而高效,通过许可证机制实现了线程的精确阻塞和唤醒。通过深入理解的源码实现,我们可以更好地掌握Java并发编程的核心机制。关键总结:许可证机制。

喜欢炒股的码农 1007

java unpark_LockSupport中的park与unpark原理

LockSupport是用来创建locks的基本线程阻塞基元,比如AQS中实现线程挂起的方法,就是park,对应唤醒就是unpark。JDK中有使用的如下LockSupport提供的是一个许可,如果存在许可,线程在调用park的时候,会立马返回,此时许可也会被消费掉,如果没有许可,则会阻塞。调用unpark的时候,如果许可本身不可用,则会使得许可可用许可只有一个,不可累加park源码跟踪park的...

weixin_35738304的博客 647

并发编程学习(六):park、unpark

它们是LockSupport类中的方法,内部调用的是Unsafe类中的native方法。RUNNINGLockSupport.unpark(要恢复的线程对象);先park再unpark的方式是容易理解的。但还有一个场景,先unpark后再次执行park方法,也不会阻塞调用了park方法的线程。理解为park方法就是校验获取一个通行令牌,而unpark方法是获取到一个通行令牌的过程。先执行unpark方法,代表先获得了通行令牌。

weixin_40482816的博客 2141

Java线程休眠的终极指南:深入解析LockSupport.park()与unpark()

本文深入解析Java并发编程中的LockSupport.park()与unpark()方法,对比Thread.sleep和Object.wait,揭示其基于许可证(Permit)的底层线程阻塞与唤醒机制。作为AQS等并发框架的核心,它提供了精准、无锁的线程协作方案,是构建高性能并发组件的关键工具。

motor的博客 907

Java的LockSupport.park()实现分析

LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语。LockSupport实际上是调用了Unsafe类里的函数,归结到Unsafe里,只有两个函数: public native void unpark(Thread jthread); public native void park(boolean isAbsolute, long ti

横云断岭的专栏 5万+

【Java】LockSupport原理与使用

每次调用park方法都会消耗一个permit。当permit数量为1个时,再次调用unpark方法不会让permit继续累加,这意味着permit的最大值为1,最小值为0。该方法的作用也是使当前线程阻塞,与无参的park相比多了一个锁对象:blocker,如果我们想让线程阻塞于某一对象,以便我们更好的了解和管理线程的阻塞状态就可以使用该方法。测试结果表明其它线程可以提前调用unpark方法,让permit数量变成1,当既定线程调用park方法时,由于permit数量不为0所以不会陷入阻塞,而是立即返回。

Annancqxxx的博客 2170

深入剖析 Java 并发基石:LockSupport 工具类

在 Java 并发编程的浩瀚宇宙中,我们熟知 synchronized关键字、ReentrantLock显式锁以及各种并发集合。然而,在这些高楼大厦之下,埋藏着一位低调但至关重要的基石工匠——LockSupport​ 工具类。

一起探索IT的世界 869

java park unpark_LockSupport(park/unpark)源码分析

concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS框架借助于两个类:Unsafe(提供CAS操作)LockSupport(提供park/unpark操作)因此,LockSupport非常重要。两个重点(1)操作对象归根结底,LockSupport.park()和LockSupport.unpark(Thread thread)调用的是Unsa...

weixin_30332165的博客 271
上一篇: 开发避坑指南(27):Vue3中高效安全修改列表元素属性的方法
下一篇: 开发避坑指南(28):Spring Boot端点检查禁用失效解决方案
帧栈
博客等级 码龄2年 335粉丝 120原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帧栈

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值