java知识总结(九)多线程

JAVA多线程、并发知识点总结 1、java多线程(一个优秀的博客)http://www.cnblogs.com/skywang12345/p/java_threads_category.html2、线程状态图http://www.cnblogs.com/skywang12345/p/3479024.html3、Callable、Future、FutureTaskhttp://blog.youkuaiyun.com/hbtj_1216/art 阅读详情

原文来自搬砖工


一、什么是进程和线程?它们有什么区别?

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
区别:
多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响. •线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。

多线程编程的目的,就是”最大限度地利用CPU资源”,当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

二、java在多线程中的基础知识

1.Java中如果我们自己没有产生线程,那么系统就会给我们产生一个线程(主线程,main方法就在主线程上运行),我们的程序都是由线程来执行的。

2. 进程:执行中的程序(程序是静态的概念,进程是动态的概念)。 

3. 线程的实现有两种方式,第一种方式是继承Thread类,然后重写run方法;第二种是实现Runnable接口,然后实现其run方法。 

4. 将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就叫做一个线程类。 

5. 一个进程至少要包含一个线程。 

6. 对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。 

7. 对于双核或双核以上的CPU来说,可以真正做到微观并行。

8.并行与并发:并行是指多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。并发是指通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

9.线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果。

10.同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。


三、线程的状态

线程状态:




线程状态转换:


各种状态一目了然,值得一提的是"blocked"这个状态,线程在Running的过程中可能会遇到阻塞(Blocked)情况:

1.调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。

2.调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)

3.对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。

注意:在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。


四、每个对象都有的方法(机制)

synchronized, wait, notify 是任何对象都具有的同步工具。


monitor

他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。

wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。

当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。

用法:

1.synchronized单独在代码块中使用

public class Thread1 implements Runnable {
   Object lock;
   public void run() {  
       synchronized(lock){
         ..do something
       }
   }
}
2.synchronized直接用于方法

public class Thread1 implements Runnable {
   public synchronized void run() {  
        ..do something
   }
}
3.synchronized, wait, notify结合:典型场景生产者消费者问题

/**
   * 生产者生产出来的产品交给店员
   */
  public synchronized void produce()
  {
      if(this.product >= MAX_PRODUCT)
      {
          try
          {
              wait();  
              System.out.println("产品已满,请稍候再生产");
          }
          catch(InterruptedException e)
          {
              e.printStackTrace();
          }
          return;
      }

      this.product++;
      System.out.println("生产者生产第" + this.product + "个产品.");
      notifyAll();   //通知等待区的消费者可以取出产品了
  }

  /**
   * 消费者从店员取产品
   */
  public synchronized void consume()
  {
      if(this.product <= MIN_PRODUCT)
      {
          try 
          {
              wait(); 
              System.out.println("缺货,稍候再取");
          } 
          catch (InterruptedException e) 
          {
              e.printStackTrace();
          }
          return;
      }

      System.out.println("消费者取走了第" + this.product + "个产品.");
      this.product--;
      notifyAll();   //通知等待去的生产者可以生产产品了
  }
外:volatile
多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。

针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。

五、基本线程类

基本线程类指的是Thread类,Runnable接口,Callable接口
Thread 类实现了Runnable接口,启动一个线程的方法:

MyThread my = new MyThread();
my.start();

Thread类相关方法:

//当前线程可转让cpu控制权,让别的就绪状态线程运行(切换)
public static Thread.yield() 
//暂停一段时间
public static Thread.sleep()  
//在一个线程中调用other.join(),将等待other执行完后才继续本线程。    
public join()
//后两个函数皆可以被打断
public interrupte()

关于中断:它并不像stop方法那样会中断一个正在运行的线程。线程会不时地检测中断标识位,以判断线程是否应该被中断(中断标识值是否为true)。终端只会影响到wait状态、sleep状态和join状态。被打断的线程会抛出InterruptedException。
Thread.interrupted()检查当前线程是否发生中断,返回boolean
synchronized在获锁的过程中是不能被中断的。

中断是一个状态!interrupt()方法只是将这个状态置为true而已。所以说正常运行的程序不去检测状态,就不会终止,而wait等阻塞方法会去检查并抛出异常。如果在正常运行的程序中添加while(!Thread.interrupted()) ,则同样可以在中断后离开代码体

Thread类最佳实践:
写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理。在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的。

Runnable

与Thread类似

Callablefuture模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDone和get。其中Future对象用来存放该线程的返回值以及状态

ExecutorService e = Executors.newFixedThreadPool(3);
 //submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.
Future future = e.submit(new myCallable());
future.isDone() //return true,false 无阻塞
future.get() // return 返回值,阻塞直到该线程运行结束

六、高级多线程控制类

Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent, 提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的Java多线程程序。

1.ThreadLocal类

用处:保存线程的独立变量。对一个线程类(继承自Thread)
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session信息。

实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。
主要方法是get()和set(T a),set之后在map里维护一个threadLocal -> a,get时将a返回。ThreadLocal是一个特殊的容器。

2.原子类(AtomicInteger、AtomicBoolean……)

如果使用atomic wrapper class如atomicInteger,或者使用自己保证原子的操作,则等同于synchronized

3.Lock类 

lock: 在java.util.concurrent包内。共有三个实现:

ReentrantLock
ReentrantReadWriteLock.ReadLock
ReentrantReadWriteLock.WriteLock

主要目的是和synchronized一样, 两者都是为了解决同步问题,处理资源争端而产生的技术。功能类似但有一些区别。

区别如下:

lock更灵活,可以自由定义多把锁的枷锁解锁顺序(synchronized要按照先加的后解顺序)
提供多种加锁方案,lock 阻塞式, trylock 无阻塞式, lockInterruptily 可打断式, 还有trylock的带超时时间版本。
本质上和监视器锁(即synchronized是一样的)
能力越大,责任越大,必须控制好加锁和解锁,否则会导致灾难。
和Condition类的结合。

4.容器类

这里就讨论比较常用的两个:

BlockingQueue
ConcurrentHashMap

BlockingQueue

阻塞队列。该类是java.util.concurrent包下的重要类,通过对Queue的学习可以得知,这个queue是单向队列,可以在队列头添加元素和在队尾删除或取出元素。类似于一个管道,特别适用于先进先出策略的一些应用场景。普通的queue接口主要实现有PriorityQueue(优先队列),有兴趣可以研究

ConcurrentHashMap
高效的线程安全哈希map。

5.管理类

管理类的概念比较泛,用于管理线程,本身不是多线程的,但提供了一些机制来利用上述的工具做一些封装。
了解到的值得一提的管理类:ThreadPoolExecutor和 JMX框架下的系统级管理类 ThreadMXBean
ThreadPoolExecutor
如果不了解这个类,使用ExecutorService,开一个自己的线程池非常方便:

ExecutorService e = Executors.newCachedThreadPool();
    ExecutorService e = Executors.newSingleThreadExecutor();
    ExecutorService e = Executors.newFixedThreadPool(3);
    // 第一种是可变大小线程池,按照任务数来分配线程,
    // 第二种是单线程池,相当于FixedThreadPool(1)
    // 第三种是固定大小线程池。
    // 然后运行
    e.execute(new MyRunnableImpl());
该类内部是通过ThreadPoolExecutor实现的,掌握该类有助于理解线程池的管理,本质上,他们都是

ThreadPoolExecutor参数解释

corePoolSize:池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。
maximumPoolSize:线程最大值,线程的增长始终不会超过该值。
keepAliveTime:当池内线程数高于corePoolSize时,经过多少时间多余的空闲线程才会被回收。回收前处于wait状态
unit:
时间单位,可以使用TimeUnit的实例,如TimeUnit.MILLISECONDS 
workQueue:待入任务(Runnable)的等待场所,该参数主要影响调度策略,如公平与否,是否产生饿死(starving)
threadFactory:线程工厂类,有默认实现,如果有自定义的需要则需要自己实现ThreadFactory接口并作为参数传入。

Java-多线程知识点 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。进程是资源分配的最小单位线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。线程是cpu调度的最小单位线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。 阅读详情

相关推荐

el-table通过上下左右键移动光标

在Element Plus的el-table中包含el-input和el-select组件,要实现上下左右键控制光标位置,需要通过focus和blur跟踪判断当前是哪个el-input或el-select有焦点,当按键按下时,通过监听,计算下一个获取焦点元素的索引,通过focus方法将焦点切换至改元素。

秋思的博客 1350

java中Math.max()的方法使用.lang

1.描述 java.lang.Math.max(double a,double b)(可以是double 类型,也可以是int类型,float类型),返回最大值。 2.声明 public static double max(double a, double b)

andy_gfz的博客 1万+

解决2020R2,2022R1等版本的SCDM无法编辑草图尺寸的问题

解决2020R2,2022R1等版本的SCDM无法编辑草图尺寸的问题

y_u_yu_yu_的博客 1万+

线性规划之单纯形解法Java实现

单纯形法理解完这个算法后大家也可以打一下,打码过程中,会有很多很多的错误要排查,编码一小时,排错三小时,不过从中可以练习到蛮多的

need_just_word的博客 8729

PowerBI参数化实战:3分钟搞定动态数据源切换(附常见错误排查)

本文详细介绍了PowerBI参数化实战技巧,教你3分钟实现动态数据源切换。通过创建参数、改造查询和测试切换三步,轻松应对多环境部署与团队协作中的路径问题,并附上常见错误排查指南,有效提升报表的可维护性和工作效率。

p1q2r的博客 585

实战经验:Sensor Bringup中那些坑我都帮你踩过了(避坑指南)

本文是一份详尽的Sensor Bringup实战避坑指南,总结了从I2C通信、MCLK时钟、电源管理到图像异常等关键环节的常见问题与排查方法。文章提供了系统化的调试工作流,强调先硬件后软件的排查原则,旨在帮助硬件工程师快速定位并解决Sensor启动过程中的难题,提升开发效率。

weixin_29212429的博客 574

2024年最新版零基础详细Java知识笔记【多线程】⑨

最新Java零基础笔记,本篇主要讲解关于多线程基础知识,包括线程概述、并发与并行、实现线程、线程的生命周期、线程的休眠与终止、守护线程、线程的调度、线程的合并、线程的优先级、让位、线程安全问题、同步机制、线程间的通信、单例模式的线程安全为题、callable实现线程、线程池实现线程等知识点。

XFanny的博客 1264

UserWarning: CUDA initialization: Unexpected error from cudaGetDeviceCount()【已解决】

UserWarning: CUDA initialization: Unexpected error from cudaGetDeviceCount()【已解决】

围炉夜敲的博客 1万+

Java知识梳理之多线程与并行程序设计(九)

部分代码的Github地址为:https://github.com/hzka/JavaBook02/tree/master/chap30(一)基础概念 1.线程的概念: Java最重要的特性就是内部支持多线程,即一个程序中允许同时执行多个任务。线程是指一个任务从头到尾的执行流程。可以在程序中创建附加的线程以执行并发任务,在Java接口中,每个任务都是Runnable...

云瀚的自习室 996

PX4 Gazebo仿真进阶:自定义飞机模型和地图的完整指南

本文详细介绍了如何在PX4 Gazebo仿真中自定义飞机模型和地图模型,包括文件架构解析、参数调整指南、地图定制技巧及常见问题解决方案。通过实战步骤和性能优化建议,帮助开发者高效完成复杂场景的飞行控制算法测试。

weixin_29081101的博客 385

【芯片设计- RTL 数字逻辑设计入门 番外篇6 -- 术语 Tile 介绍】

在系统级芯片(System on Chip, SoC)设计中,“tile” 是一个可以指代不同概念的术语,但通常它指的是芯片上的一个独立的功能单元或核心。一个 tile 可以包含一个或多个处理器核心、缓存、内存控制器、输入输出设备、外设接口或其他功能模块。在 SoC 上,多个 tile 可以被组合和连接起来,以形成一个完整的系统。

CodingCos的博客 3065

Java知识整理(九)之SE.多线程

多线程(t.start();后才执行run()方法): 进程与线程的区别:一个进程可以包含多个线程,进程有独立内存单元,线程共享内存单元 并发原理:获取时间片的线程被CPU运行,其他线程全部等待 使用Thread创建并启动线程(继承Thread并重写run()方法): class MyThread extends Thread{ public void run(){ System.ou

qq619771478的博客 368

3.38 工业缺陷检测实战:物体表面缺陷检测,从图像处理到深度学习

本文介绍了工业缺陷检测的完整流程,从传统图像处理到深度学习方法。首先定义了缺陷检测任务的目标和应用场景,然后详细讲解了传统图像处理方法(边缘检测、阈值分割等)和深度学习方法(分类、检测、分割模型)。文章最后总结了核心要点,提出了思考题和实践建议,建议从简单方法开始逐步优化。下一篇将介绍工业视觉基础技术——相机标定与角点检测。

yonggeit的博客 2885

Java多线程知识点总结——进阶篇(九) 之 停止线程

如何停止线程? 只有一种,run方法结束。 开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。 注意:stop方法已经过时。 实例代码如下: class StopThread implements Runnable { private boolean flag = true; //控制循环是否退出的标志 ...

瓜牛呱呱的博客 347

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Java 面试题及答案整理(2025 年金九银十最新版)

本文整理了Java工程师面试常见问题大全,涵盖MyBatis、Zookeeper、Redis、MySQL、并发编程等核心知识点。内容包含Java基础、容器、多线程、反射等关键领域,详细解析了JDK/JRE区别、==与equals比较、HashMap原理、线程池使用等高频考点。同时提供了Spring、微服务、Linux等专题面试题,帮助开发者系统准备技术面试。附赠全套面试笔记获取方式,适合正在求职的Java开发者参考学习。

2401_89221445的博客 1420

廖雪峰Python教程[完整版]

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

2024年金九银十最新版Java面试题及答案整理(持续更新)

wait():wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程。线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。不一样,因为内存的分配方式不一样。创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。

qq13321123的博客 2176

2022年金九银十Java面试手册宝典,横扫春招+校招+社招+秋招

5.GC的常见算法,CMS以及G1的垃圾回收过程,CMS的各个阶段哪两个是Stop the world的,CMS会不会产生碎片,G1的优势。4.MySQL的MyISAM与InnoDB两种存储引擎在,事务、锁级别,各自的适用场景?3.MySQL常见的三种存储引擎(InnoDB、MyISAM、MEMORY)的区别?5、HashMap1.7与1.8的 区别,说明1.8做了哪些优化,如何优化的?4、4、如何重新加载Spring Boot上的更改,而无需重新启动服务器?

m0_73256420的博客 290

【Java基础知识8】多线程与并发

介绍多线程之前要介绍线程,而介绍线程则离不开进程1.进程在计算机中,我们把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另一个进程,类似的,音乐播放器和Word都是进程。

阿志的博客 1368

Java多线程基础知识总结

<-----------------------------------------廖雪峰学Java-----------------------------------------> 目录1. 多线程基础知识2. 创建多线程3. 线程状态4. 中断线程5. 守护线程6. 线程同步7. 同步方法8. 死锁9. 使用wait和notify10. 使用ReentrantLock11. 使用Condition12. 使用ReadWriteLock13. 使用StampedLock14. 使用Concu

Pinkgranite86的博客 2721

Java多线程的知识点

Java多线程的知识点

weixin_59796310的博客 5279

Java多线程知识点深度总结

一般来说,核心线程数可以设置为系统可以承受的同时处理的任务数,最大线程数可以设置为系统可以承受的最大并发任务数,队列容量则可以根据任务的生成速度和处理速度来设定。如果队列满了,而线程池中的线程数还没有达到最大线程数,则会创建新的线程来执行任务。当线程池中的线程数超过处理任务所需的线程数时,空闲的线程会在指定的时间内自动销毁,以减少系统开销。如果当前线程池中的线程数小于核心线程数,即使线程池中的其他线程是空闲的,也会创建一个新线程来处理新提交的任务。>类型的任务,表示延迟执行或定期执行的任务。

种一棵树,最好的时间是十年前,其次是现在。 1320

Java多线程面试知识点整理

Java多线程知识点整理1.1 线程与进程1.2 线程和进程有什么区别?1.3 常用的开启多线程的技术?1.继承Thread类,实现run()方法2.实现Runnable接口,实现run()方法实现Runnable 与 继承Thread相比的优势3.实现Callable接口,实现call()方法。Runnable 与 Callable 的区别1.4 并行与并发1.5 Thread 类中的start() 和 run() 方法有什么区别?1.6 Java多线程知识点整理 1.1 线程与进程 进程:   是资源.

weixin_41758106的博客 910

Java知识点整理 2 — 多线程

Java知识点整理 2 — 多线程部分 后续会持续学习更新。

konghaoran的博客 1162

Java:多线程相关知识概念

多线程编程是Java中一个高级且复杂的主题,它要求开发者对线程的创建、管理、同步和通信有深入的理解。正确地使用多线程可以显著提高应用程序的性能和响应能力,但同时也需要注意避免常见的并发问题,如死锁和竞态条件。通过遵循最佳实践和使用Java提供的并发工具,可以编写出高效且健壮的多线程应用程序。

m0_65888394的博客 2686

Java 多线程知识点

在 Java 并发编程中,线程优先级是一个重要的调度属性。在 Java 并发编程中,频繁创建和销毁线程会消耗大量系统资源,不仅增加 JVM 内存开销,还会导致频繁的上下文切换,影响系统性能。在实际开发中,推荐优先使用 Runnable 或 Callable 接口的方式,因为它们更灵活,更符合面向接口编程的原则,也更容易与 Java 并发工具类集成。Thread 类是 Java 中线程操作的核心类,继承该类需要重写 run() 方法(线程执行体),通过调用 start() 方法启动线程。

Toroto5的博客 1238
上一篇: java知识总结(八)I/O流操作
下一篇: java知识总结(十)反射和代理
海之沐
博客等级 码龄12年 1536粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值