深入理解Lock

多线程 - 为什么要用读写锁?它有什么优点?以及使用案例 【为什么要用读写锁?它有什么优点?以及使用案例】 阅读详情

Synchronized在大多数情况下已经能够满足需求,但是仍然存在这样的缺点:无法中断正在获取锁的线程,并且在请求失败的情况下需要无限等待
与Synchronized相比,Lock锁更加灵活,当我们需要的锁是可定时的,可轮询的(while trylock),可中断的锁操作的时候,我们应该选择Lock锁,下面是Lock锁的接口声明

public interface Lock {

    void lock();
    //尝试获取锁,若成功返回true否则返回false
    boolean tryLock();

    //在尝试获取锁期间可以被中断
    void lockInterruptibly() throws InterruptedException;

    //尝试在指定时间内获取锁,若成功返回true否则返回false
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

Reentrant提供了与Synchronized一样的可重入加锁的功能.

公平锁与非公平锁

ReentrantLock通过构造函数可以选择创建非公平锁和公平锁。在竞争激烈的情况下,闯入锁比公平锁性能上要好接近100倍.原因在于线程从挂起状态到真正开始运行会有严重的延迟,比如A线程占用了某个锁lock,B请求申请该锁,但是由于lock锁正在被占用于是将B线程挂起,A释放锁时,B线程从挂起状态恢复,这个时候C请求该锁,由于B存在延时所以C得到了该锁.
但是公平锁也有用武之地,当持有锁的时间较长,或者请求锁的平均时间间隔比较长,那么使用公平锁是比较好的

读写锁

在有些应用中读请求远大于写请求,而Reentrant型互斥限制性太强,避免了写/写和读/写同时也避开了读/读,而读请求不会修改共享变量数据结构,所以需要一种锁允许线程并发读数据提高并发性能.下面是读写锁的接口声明.

public interface ReadWriteLock {

    Lock readLock();

    Lock writeLock();
}

读写锁分为读锁和写锁,多个读锁不互斥,读锁和写锁互斥,这是由JVM控制的,我们只需要记住在读数据的时候加读锁写数据的时候加写锁就可以了。ReentrantReadWriteLock就是读写锁的一种实现,ReentrantReadWriteLock使用两把锁解决问题,一个读锁,一个写锁。
在公平的ReentrantReadWriteLock中,获取锁的条件为:

线程进入读锁的前提条件
1. 没有其他线程申请写锁
2. 没有写请求

线程进入写锁的前提条件
1. 没有其它线程的读锁
2. 没有其它线程的写锁

在非公平的ReentrantReadWriteLock中

线程的访问顺序是不定的,从写者降级为读者是可以的,但从读者升级为写者是不允许的,因为这会造成死锁,比如两个读者同时试图升级到同一个写入锁但是都不释放读锁

我们利用读写锁构建一个支持并发的HashMap数据结构

public class ReadWriteMap<K, V> {
    private final Map<K, V> map;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public ReadWriteMap() {
        map = new HashMap<>();
    }

    public V put(K key, V value) {
        //修改数据结构之前先获取写锁
        writeLock.lock();
        try {
            return map.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }

    public V get(K key) {
        //读取数据之前要先获取读锁
        readLock.lock();
        try {
            return map.get(key);
        } finally {
            readLock.unlock();
        }
    }

}
深入理解Java中的Synchronized与Lock 是Java中实现多线程同步控制的关键字,用于控制对共享资源的并发访问。当一个方法或代码块被修饰时,同一时刻只能有一个线程执行该段代码,从而确保了数据的一致性。它是基于对象的监视器(monitor)来实现的,每个对象都有一把锁与之关联。在内部,synchronized的实现依赖于Java虚拟机(JVM)的对象头。对象头中包含了一个名为Mark Word的区域,它存储了锁标志和指向同步监视器的指针。当一个线程试图访问同步代码块时,JVM会检查对象头中的锁标志位,确定对象是否被锁。 阅读详情

相关推荐

锁实现lock

C实现线程间的数据锁问题 lock.cpp lock.h

Lock用法和加锁原理

note: 1)加锁语句lock.lock();catch语句中发生的,这两个部分可能发生的异常不影响finally执行。synchronized同步机制是依赖JVM实现,Lock主要依赖抽象队列同步器(AbstractQueuedSynchronier,AQS)实现的,AQS是一个双向链表,使用一个整型变量state表示对共享区域的加锁状态,对于每个尝试访问临界区的线程都会被封装成双向链表中的一个节点,主要通过tryacquired获取临界区访问权,通过tryrelease释放临界区访问权。

huhu123444的博客 685

关于synchronized、Lock的深入理解

目录synchronized的缺陷Lock和ReentrantLock常用方法ReadWriteLock和ReentrantReadWriteLockLock和synchronized区别synchronized锁升级公平锁和非公平锁 synchronized的缺陷 众所周知,synchronized锁是JAVA的关键字,按理说是JAVA语言内置的特性,那为什么还要使用Lock呢 我们先说一说synchronized,当一个方法或者代码块被synchronized修饰,并执行到此方法或者代码块时,获取到锁并执行,其他线程进来拿不到锁就会一直等待,等待获取到锁的线程释放锁。而这里获取到锁的线程释

读写锁 作用

读写锁

一只不知疲倦的学习猿 427

SPI模式下的SD卡读写,为什么你的数据总出错?详解时序与状态机避坑指南

本文深入探讨SPI模式下SD卡读写数据出错的根本原因,提供时序优化与状态机设计的实战指南。针对时钟周期计数、状态机跳转和忙检测等核心问题,详解命令时序细节、状态机同步策略及高级避坑技巧,帮助开发者提升SD卡在SPI模式下的稳定性和性能。

weixin_30514745的博客 263

AI工程实战:如何在实际工程中运用Claude Code

本文探讨了利用大型语言模型(LLM)进行复杂工程项目的结构化工作流。核心策略包括:1)规划先行,通过"探索子代理任务"主动分析代码库并澄清需求;2)定制AI行为,要求简洁输出和列出未解决问题;3)将大型任务分解为多阶段计划以管理上下文窗口限制;4)利用GitHub Issues作为外部存储实现跨会话状态持久化。这种"规划-执行"模式将AI从代码生成器升级为项目管理代理,通过与GitHub CLI等工具深度集成,实现了高效的多阶段工程协作。该方法展示了AI辅助编程向流程

老孙 | Ethan Sun 的博客:AI 智能体与 AI 原生软件的工程实践 1098

读写锁详细介绍

锁降级是指从写锁降级为读锁的过程,这是允许的,但锁升级(从读锁升级为写锁)通常是不允许的。锁降级通常是为了在修改共享资源后,保持一定的读访问,而不完全释放对资源的保护。其工作原理如下持有写锁一开始,线程需要持有写锁,以进行写操作。这是因为写操作需要独占访问共享资源,防止数据不一致获取读锁在持有写锁的同时,线程可以尝试获取读锁。由于线程已经持有写锁,因此获取读锁不会被阻塞。此时,线程同时持有写锁和读锁。释放写锁在获取读锁之后,线程可以释放写锁,此时线程仍然持有读锁。

Kaiser__的博客 1098

cadence virtuoso 激励信号源

vpulse,vpwl,vsin,vsource信号源介绍

努力学习的IC小白 3万+

lockc 项目常见问题解决方案

lockc 项目常见问题解决方案 项目基础介绍 lockc 是一个开源项目,旨在通过 eBPF 和 Linux Security Modules (LSM) 技术增强容器的安全性。项目的主要目标是提供更强的隔离性和安全性,以防止容器中的恶意行为。lockc 的核心技术是 eBPF,特别是其能够附加到 LSM 钩子的能力。项目目前处于实验阶段,尚未提供官方的二进制文件或包,因此用户需要从源代码构建。...

gitblog_00060的博客 345

手把手教你用Apple Developer App完成苹果开发者账号注册(含证件验证失败解决方案)

本文详细指导如何通过Apple Developer App完成苹果开发者账号注册,特别针对证件验证失败问题提供解决方案。从准备工作到注册流程,再到验证失败的处理方法,帮助开发者顺利完成注册,避免常见错误。

moss5的博客 1161

YOLO11全网首发优化:卷积魔改创新 | AAAI 2025 | 一种新颖的风车形卷积(PConv)符合微弱小目标的像素高斯空间分布,增强特征提取,显著增加接受野

我们提出了一种新颖的风车形卷积(PConv)作为骨干网络下层标准卷积的替代品。PConv能更好地符合微弱小目标的像素高斯空间分布,增强特征提取,显著增加接受野,且只引入最小的参数增加。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 1973

mysql lock scope_技术分享 | 深入理解 MySQL MDL Lock

作者:高鹏(网名八怪) 文章末尾有他著作的《深入理解 MySQL 主从原理 32 讲》,深入透彻理解 MySQL 主从,GTID 相关技术知识。 本文来源:转载自公众号-mysql code tracer *爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。本文基于源码版本 5.7.14水平有限,有误请谅解。笔者已经将加好 MDL 获取过程和释放过程的版本放到了githu...

weixin_42237487的博客 312

在Centos上从零开始部署并运行TinyWebServer服务器,过程记录(非常详细)

TinyWebServer是个什么项目?它是Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器。使用线程池+非阻塞socket+epoll(ET和LT均实现) +事件处理(Reactor和模拟Proactor均实现) 的并发模型使用状态机解析HTTP请求报文,支持解析GET和POST请求访问服务器数据库实现web端用户注册、登录功能,可以请求服务器图片和视频文件实现同步/异步日志系统,记录服务器运行状态。......

AnChenliang_1002的博客 3021

C#中的Lock锁深入理解

lock语句 lock语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。阻止任何其他线程获取 lock 并等待释放 lock。 为什么需要锁 作为C#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字。lock 的目的很明确,就是不想让别人使用这段代码,体现在多线程情况...

u011555996的博客 2万+

使用R语言实现循环神经网络RNN进行文本生成

首先,让我们回顾一下循环神经网络(Recurrent Neural Networks,RNN)的基本概念。RNN是一种深度学习技术,特别适合处理序列数据,如时间序列和文本数据。RNN的关键特性是它的隐藏状态,这个状态可以捕捉到当前输入之前的信息。这使得RNN能够利用前面的信息来影响后续的输出,从而对序列数据进行有效的处理。

斌擎科技 726

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

深入理解Lock接口

Lock的使用 lock与synchronized的区别 lock ​ 获取锁与释放锁的过程,都需要程序员手动的控制 ​ Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就 是CAS操作 ​ synchronized托管给jvm执行 ​ 原始采用的是CPU悲观锁机制,即线程获得的是独占锁。...

zssz1026的专栏 249

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

深入理解Lock的底层实现原理

lock的实现完全是由java写的,和操作系统或者是JVM虚拟机没有任何关系。整体来看Lock主要是通过两个东西来实现的分别是CAS和ASQ(AbstractQueuedSynchronizer)。通过加锁和解锁的过程来分析锁的实现。 加锁 一、整体概述流程 1. 读取表示锁状态的变量 2. 如果表示状态的变量的值为0,那么当前线程尝试将变量值设置为1(通过CAS操作完成),当多个线程

石头视角 1万+

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

深入理解c#中的lock

<br />lock只能使用引用类型,严格来说是需要对象的实例。<br />即使对象在意义上是相同的,但是如果不是ReferenceEquals的话,那么将作为两个实例来对待,那么C# lock 的就不是同一个东西。也就是说,当你以为这个 lock 生效的话,它其实在做无用工。<br />测试用例:<br />using System; using System.Threading; class TTT { private hello test; public TTT(he

追求卓越,成功会不经意间追上你。 641

Java多线程之Lock深入理解

文章目录1 Lock原理深入理解1.1 synchronized的缺陷1.2 java.util.concurrent.locks包下常用的类1.2.1 Lock接口1.2.1.1 lock方法1.2.1.2 tryLock方法1.2.1.3 lockInterruptibly方法1.2.1.4 newCondition方法1.2.2 ReentrantLock1.2.2.1 可重入锁概念1.2.2.2 ReentrantLock使用1.2.2.2.1 lock用法1.2.2.2.2 tryLock用法1.

上善若泪 2479

深入理解 synchronized、Lock 与死锁

Lock死锁是指两个或多个线程互相持有对方所需的资源,导致所有线程都无法继续执行的状态。:简单易用,适合低竞争场景。Lock:功能强大,适合高竞争场景。死锁:通过破坏死锁的四个必要条件来避免。

秃头之旅 887

Java多线程之Lock使用深入理解

Lock锁的使用

tian830937的专栏 2461

深入理解Lock Support

Lock Support,听起来是不是有点像是某个高大上的技术?其实它就是Java.util.concurrent包里的一个工具类。park()和unpark()。这两个小伙伴,一个负责让线程停下来,另一个负责让线程继续跑。听起来很简单对吧?但它们可是大有来头。在Java里,锁和同步是常见的话题。传统的同步工具像和都是阻塞式的,意味着当一个线程获取不到锁时,就会进入阻塞状态,等待唤醒。这种方式虽然简单,但有时候效率不高,尤其是在高并发场景下。这时,Lock Support就闪亮登场了。

宋小黑的博客 1160

并发编程之深入理解Lock接口

写一个线程不安全的代码: public class UnsafeThread { private static int num = 0 ; private static CountDownLatch countDownLatch = new CountDownLatch(10); public static void inCrease(){ num++; ...

不怂,不弱,不退缩,厚积薄发 245

深入理解Java中的Lock和AQS

提起Java中的锁,一般我们最快的反应是。但是在Java1.5之后,Doug Lea 大姥设计并实现的 JUC 中,提供了更加丰富的API操作。其中Lock接口及其相关实现尤为经典。今天我们来一起学习这 JUC 中的优秀设计思想。与有锁不同,Lock接口及其实现是由Java代码实现的。其底层代码实现,基于抽象队列同步器()以及volatile关键字、CAS机制。以下内容分析,代码参考于Java11。因为Lock本身是一个接口,所以我们在用的时候,基本都是找它的实现。

FBB360JAVA的博客 981
上一篇: AtomicInteger理解和使用
下一篇: java编程思想笔记---并发Callable接口
光与热
博客等级 码龄11年 55粉丝 126原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值