java编程思想笔记-并发之并发锁synchronized详解(二)

synchronized用法详解 目录1.线程安全问题2.使用同步机制管理共享数据3.synchronized原理概述3.1 同步代码块的实现原理3.2 同步方法的原理4.synchronized的使用4.1同步代码块 4.2同步普通方法4.3同步静态方法5.synchronized的不可中断性与可重入性5.1 不可中断性5.2 可重入性6.使用synchronized的注意事项6.1注意synchronized同步块的粒度 6.2 对 String 加锁 6.3 对Integer、Long、Short等包装类加锁 线程允许程 阅读详情

《编程思想之多线程与多进程(1)——以操作系统的角度述说线程与进程》一文详细讲述了线程、进程的关系及在操作系统中的表现,这是多线程学习必须了解的基础。本文将接着讲一下Java线程同步中的一个重要的概念synchronized.

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。


修饰一个代码块

  1. 一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。我们看下面一个例子:

【Demo1】:synchronized的用法

/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;

   public SyncThread() {
      count = 0;
   }

   public  void run() {
      synchronized(this) {
         for (int i = 0; i < 5; i++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }

   public int getCount() {
      return count;
   }
}
 

SyncThread的调用:

SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "SyncThread1");
Thread thread2 = new Thread(syncThread, "SyncThread2");
thread1.start();
thread2.start();
 
  • 1
  • 2
  • 3
  • 4
  • 5

结果如下:

SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9*

当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
我们再把SyncThread的调用稍微改一下:

Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
thread1.start();
thread2.start();
 

结果如下:

SyncThread1:0
SyncThread2:1
SyncThread1:2
SyncThread2:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread1:7
SyncThread1:8
SyncThread2:9

不是说一个线程执行synchronized代码块时其它的线程受阻塞吗?为什么上面的例子中thread1和thread2同时在执行。这是因为synchronized只锁定对象,每个对象只有一个锁(lock)与之相关联,而上面的代码等同于下面这段代码:

SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();>
 

这时创建了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);我们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。


2.当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。
【Demo2】:多个线程访问synchronized和非synchronized代码块

class Counter implements Runnable{
   private int count;

   public Counter() {
      count = 0;
   }

   public void countAdd() {
      synchronized(this) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }

   //非synchronized代码块,未对count进行读写操作,所以可以不用synchronized
   public void printCount() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + " count:" + count);
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }

   public void run() {
      String threadName = Thread.currentThread().getName();
      if (threadName.equals("A")) {
         countAdd();
      } else if (threadName.equals("B")) {
         printCount();
      }
   }
}
 

调用代码:

Counter counter = new Counter();
Thread thread1 = new Thread(counter, "A");
Thread thread2 = new Thread(counter, "B");
thread1.start();
thread2.start();
 

结果如下:

A:0
B count:1
A:1
B count:2
A:2
B count:3
A:3
B count:4
A:4
B count:5

上面代码中countAdd是一个synchronized的,printCount是非synchronized的。从上面的结果中可以看出一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。


  1. 指定要给某个对象加锁

【Demo3】:指定要给某个对象加锁

/**
 * 银行账户类
 */
class Account {
   String name;
   float amount;

   public Account(String name, float amount) {
      this.name = name;
      this.amount = amount;
   }
   //存钱
   public  void deposit(float amt) {
      amount += amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   //取钱
   public  void withdraw(float amt) {
      amount -= amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }

   public float getBalance() {
      return amount;
   }
}

/**
 * 账户操作类
 */
class AccountOperator implements Runnable{
   private Account account;
   public AccountOperator(Account account) {
      this.account = account;
   }

   public void run() {
      synchronized (account) {
         account.deposit(500);
         account.withdraw(500);
         System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
      }
   }
}
 

调用代码:

Account account = new Account("zhang san", 10000.0f);
AccountOperator accountOperator = new AccountOperator(account);

final int THREAD_NUM = 5;
Thread threads[] = new Thread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i ++) {
   threads[i] = new Thread(accountOperator, "Thread" + i);
   threads[i].start();
}
 

结果如下:

Thread3:10000.0
Thread2:10000.0
Thread1:10000.0
Thread4:10000.0
Thread0:10000.0

在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。
当有一个明确的对象作为锁时,就可以用类似下面这样的方式写程序。

public void method3(SomeObject obj)
{
   //obj 锁定的对象
   synchronized(obj)
   {
      // todo
   }
}
 

当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

class Test implements Runnable
{
   private byte[] lock = new byte[0];  // 特殊的instance变量
   public void method()
   {
      synchronized(lock) {
         // todo 同步代码块
      }
   }

   public void run() {

   }
}
 
>

说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

修饰一个方法

Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//todo}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。如将【Demo1】中的run方法改成如下的方式,实现的效果一样。

*【Demo4】:synchronized修饰一个方法

public synchronized void run() {
   for (int i = 0; i < 5; i ++) {
      try {
         System.out.println(Thread.currentThread().getName() + ":" + (count++));
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
}
 

Synchronized作用于整个方法的写法。
写法一:

public synchronized void method()
{
   // todo
}
 
  • 1
  • 2
  • 3
  • 4

写法二:

public void method()
{
   synchronized(this) {
      // todo
   }
}
 

写法一修饰的是一个方法,写法二修饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个方法时的内容。

在用synchronized修饰方法时要注意以下几点:
1. synchronized关键字不能继承。
虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。这两种方式的例子代码如下:
在子类方法中加上synchronized关键字

class Parent {
   public synchronized void method() { }
}
class Child extends Parent {
   public synchronized void method() { }
}
 

在子类方法中调用父类的同步方法

class Parent {
   public synchronized void method() {   }
}
class Child extends Parent {
   public void method() { super.method();   }
} 
 
  1. 在定义接口方法时不能使用synchronized关键字。
  2. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。


修饰一个静态的方法

Synchronized也可修饰一个静态方法,用法如下:

public synchronized static void method() {
   // todo
}
 

我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。我们对Demo1进行一些修改如下:

【Demo5】:synchronized修饰静态方法

/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;

   public SyncThread() {
      count = 0;
   }

   public synchronized static void method() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + ":" + (count++));
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }

   public synchronized void run() {
      method();
   }
}
 

调用代码:

SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();
 

结果如下:

SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9

syncThread1和syncThread2是SyncThread的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁。这与Demo1是不同的。



修饰一个类

Synchronized还可作用于一个类,用法如下:

class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}
 

我们把Demo5再作一些修改。
【Demo6】:修饰一个类

/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;

   public SyncThread() {
      count = 0;
   }

   public static void method() {
      synchronized(SyncThread.class) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }

   public synchronized void run() {
      method();
   }
}
 

其效果和【Demo5】是一样的,synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁。



总结:

A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。



参考资料:
http://blog.youkuaiyun.com/chenguang79/article/details/677720
http://developer.51cto.com/art/200906/132354.htm

深入解析Java中的synchronized关键字 接下来是偏向锁,我们还是用同样的代码即可,需要注意的是偏向锁必须在jvm启动后的一段时间才会运行,所以如果我们想打印偏向锁必须让线程休眠那么几秒,这里笔者就偷懒了一下,通过设置jvm参数。打印结果如下,我们只需关注第一行的object header,可以看到第一列的00000001,我们看到后3位为001,偏向锁标记为0,锁标记为01,001这就是我们说的无锁状态。上文讲解锁升级的之后,我们一直在说对象头的概念,所以为了能够直观的看到锁对象中对象头锁标记和锁状态的变化,我们这里引入一个jol工具。 阅读详情

相关推荐

Java synchronized(详细)

在Java中,`synchronized`关键字用于解决多线程并发访问共享资源时可能出现的线程安全问题。

小电玩 2035

synchronized详解

synchronized详解

codedot 8万+

Java并发-synchronized, 偏向锁, 轻量级锁详解

synchronized概述 synchronized就是所谓的重量级锁, 但是自从jdk1.6引入了偏向锁, 轻量级锁之后, synchronized就没有那么重了。 synchronized用法 对于普通同步方法,锁是当前实例对象 对于静态同步方法,锁是当前类的Class对象 对于同步方法块,锁是Synchonized括号里配置的对象 synchronized实现原理 任何对象都有一...

听雪楼 1705

Java同步synchronized

一. synchronized是什么? synchronized是Java的一个关键字,synchronized依赖于JVM具体实现。 synchronized的作用 synchronized也称为同步锁。synchronized是一种独占锁。 synchronized的两大功能: 内存可见性 操作原子性 什么是内存可见性? 可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 可参考《Java内存分级和指令重排序》。volatile也可实现内存

joedan0104的专栏 5904

Java并发编程:Synchronized底层优化(轻量级锁、偏向锁)

否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)。如果竞争失败,执行(4)。

2401_84433600的博客 1237

Java高并发编程实战4,synchronized与Lock底层原理

Java高并发编程实战系列,打造精品专栏。

学Java,找哪吒 5220

【Java并发编程】描述常见的锁策略、CAS在Java中的实现以及 Synchronized 的加锁原理

描述常见的锁策略、描述CAS的原理以及在Java中有哪些实现、描述Java中 synchronized的基本特点和加锁原理。

yican2580的博客 2079

2024年上半年网络工程师下午真题及答案解析

位于MPLS域边缘、连接其他网络的LSR称为( 3 ),区域内部的LSR称为核心LSR(CoreLSR)IP报文进入MPLS网络时,MPLS入口设备分析IP报文的内容并为其添加合适的标签,所有MPLS网络中的LSR根据标签转发数据,当该IP报文离开MPLS网络时,标签由出口LER弹出。LSP的出口LER称为( 6 )。某高校网络拓扑如下图所示,两校区核心(CORE-1、CORE-2),出口防火墙(NGFW-1、NGFW-2)通过校区间光缆互联,配置OSPF实现全校路由收敛,两校区相距40km。

bsefef的博客 2476

【Java】synchronized关键字详解:从字节码到对象头与锁升级

synchronized底层原理详解 synchronized实现同步的核心机制是通过对象监视器(Monitor)实现的,主要包含以下关键点: 字节码层面 同步代码块编译后会生成monitorenter和monitorexit指令 同步方法通过ACC_SYNCHRONIZED标志实现 对象头与Monitor 每个对象关联一个Monitor对象 对象头中的Mark Word存储锁状态信息 Monitor包含Owner(持有线程)、EntryList(阻塞队列)和WaitSet(等待队列) 锁升级优化 无锁 →

先做到 再看见 4588

基于PSO-BP神经网络的风电功率预测研究论文复现

风电功率预测。基于PSO-BP神经网络的风电功率预测论文复现。matlab程序,注释清晰。

weixin_825487230的博客 2295

Zynq 双核AMP 通信

4、配置离线编译(这里不讲),配置linux的内核地址空间(0x4000_0000改为0x2000_0000),以及单核启动。CPU1设置为0x2000_0000~0x3FFF_FFFF(也可以小于0x3FFF_FFFF)如果禁用CPU1的L2 cache,在CPU1的bsp里面添加编译时候所需要的宏。3、配置工程,将vivado产生的.xsa文件复制到amp_com项目目录下。9、使用petalinux-package --boot 打包。一、vivado工程。5、编译u-boot。

y183128184的博客 1088

【Java并发】synchronized锁详解

在Java中,synchronized关键字是一种同步机制,用于控制多个线程对共享资源的访问,以保证在任意时刻只有一个线程可以执行特定的代码段。本文总结了synchronized的概念、用法、锁升级和特点。

hrh的博客 1998

STM32F407硬件IIC驱动OLED避坑实录:从‘不显示’到‘稳定刷屏’的完整调试过程

本文详细记录了使用STM32F407硬件IIC驱动OLED屏幕的完整调试过程,从初始的‘不显示’问题到最终实现‘稳定刷屏’。文章涵盖了硬件接线、I2C外设配置、常见问题排查及性能优化技巧,特别针对STM32标准库开发中的难点提供了实用解决方案,是嵌入式开发者驱动OLED屏幕的实用指南。

weixin_30315723的博客 304

【Java并发】深入理解synchronized

【Java并发】深入理解synchronized

2403_87459748的博客 1832

宋浩线性代数笔记

Miao-A-SongHao-Linear-Algebra-Notes bilibili 宋浩老师 “惊叹号” 系列 《线性代数》网课 笔记及时间点目录 Github项目地址:https://github.com/cy69855522/Miao-A-SongHao-Linear-Algebra-Notes ???? 前言 我发现吧,线代没记笔记真不行。 浩浩学习,天天向上。数学网课推荐????宋浩老师的免...

Knife丶小刀博客 4万+

ArcGIS实验教程——实验二十五:大型商场选址经典案例

本实验基于ArcGIS平台进行商场选址分析,综合考虑道路、居民点、停车场和已有商场四个影响因素。通过缓冲区分析建立各要素的影响范围(道路50米、居民点100米、停车场100米、已有商场500米),采用叠加分析中的相交和擦除运算确定最优选址区域。实验建立了区位评价体系,通过属性赋值(1/-1)和联合运算生成综合评价结果,并实现分级可视化显示。注意事项包括距离参数设置对结果的影响以及正确选择空间分析工具。该流程为商业选址提供了科学的GIS解决方案。

智慧空间实验室 1万+

java并发编程—synchronized 的基本用法和锁升级机制详解

的锁升级机制是 Java 并发编程中的重要优化,它通过偏向锁轻量级锁和重量级锁的逐步升级,在保证线程安全的同时,尽量减少锁带来的性能开销。开发者可以根据具体的并发场景,理解锁升级的原理,从而编写出更高效的并发程序。

Future_yzx的博客 3万+

基于Simulink环境,提供一个关于如何对EMI抑制的DC-DC变换器进行仿真建模的示例

EMI定义:电磁干扰是指任何能中断、阻碍、降低或限制电气设备有效性能的电磁能量。重要性合规性:确保产品符合国际和地方的EMC标准。可靠性:减少因外部或内部EMI导致的系统故障风险。挑战滤波设计:合理设计输入输出滤波器以抑制传导EMI。布局与接地:良好的PCB设计对于减少辐射EMI至关重要。屏蔽:使用金属屏蔽来保护敏感电路不受外界干扰。首先明确要模拟的任务内容。设计一个能够在高频操作下有效抑制EMI的DC-DC变换器。根据工作频率和谐振元件值确定合适的滤波器参数。

xiaoheshang_123的博客 148

Java并发编程——Synchronized锁升级机制

Synchronized锁升级流程

BUPTZhanggg的博客 2002

使用 MATLAB/Simulink + Simscape Electrical + Simulink Design Optimization,构建一个沙漠环境中光伏组件灰尘积累与效率下降仿真模型

本文通过,构建了沙漠环境中光伏组件灰尘积累与效率下降仿真模型✅ 建立了风速-沙尘-积尘速率的物理关系✅ 仿真了灰尘积累过程及其对Isc 和 Pmax的影响✅ 量化分析了不同清洁策略的经济性✅ 提出了优化设计与智能运维建议。

xiaoheshang_123的博客 123

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java并发之synchronized详解

带你详细学习Java中的synchronized关键字,包括原理与使用。

个人博客,欢迎收藏。 1358

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Synchronized锁详解

Synchronized锁详解

lixiangchibang的博客 5092

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

从 synchronized 到 CAS 和 AQS - 彻底弄懂 Java 各种并发锁

概述 Java 中的并发锁大致分为隐式锁和显式锁两种。隐式锁就是我们最常使用的 synchronized 关键字,显式锁主要包含两个接口:Lock 和 ReadWriteLock,主要实现类分别为 ReentrantLock 和 ReentrantReadWriteLock,这两个类都是基于 AQS(AbstractQueuedSynchronizer) 实现的。还有的地方将 CAS 也称为一种...

u010325193的博客 5249

【Java并发编程】synchronized详解

深入解析了 Java 中 synchronized 关键字的原理,从源码层面揭示了其背后的实现机制和优化策略。

叉烧钵钵鸡的博客 4126

Java 并发编程中的 synchronized 锁详解

是 Java 中实现线程安全的一个重要工具,它能够有效地防止多个线程同时访问共享资源导致的数据不一致问题。然而,也存在性能开销和死锁风险,因此在实际开发中,需要合理设计和使用。对于简单的线程安全需求,可以直接使用。对于复杂的并发场景,应该考虑使用更灵活的工具,如Atomic等。

萌萌白白的博客 825
上一篇: 线程安全的单例模式
下一篇: ExecutorService对象的shutdown()和shutdownNow()的区别
光与热
博客等级 码龄11年 55粉丝 126原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值