灵魂发问!线程池到底创建多少线程比较合理?

一位大厂面试官的灵魂发问:Executor 和 Executors 有什么区别? 就在上周,我又去面了一家大厂。“你说说 Executors 和 ThreadPoolExecutor 的区别。我一笑,娓娓道来,从源码到风险,再到推荐方案,说得对方频频点头。“你这回答比我们内部文档都清楚。被一道题打败,不是终点;弄懂它,讲出来,就是你进阶的开始。 阅读详情

虽然线程池的模型被剖析的非常清晰,但是如何最高性能地使用线程池一直是一个令人纠结的问题,其中最主要的问题就是如何决定线程池的大小。这篇文章会以量化测试的方式分析:何种情况线程池应该使用多少线程数。

计算密集型任务与IO密集型任务

大多数刚接触线程池的人会认为有一个准确的值作为线程数能让线程池适用在程序的各个地方。然而大多数情况下并没有放之四海而皆准的值,很多时候我们要根据任务类型来决定线程池大小以达到最佳性能。

计算密集型任务以CPU计算为主,这个过程中会涉及到一些内存数据的存取(速度明显快于IO),执行任务时CPU处于忙碌状态。

IO密集型任务以IO为主,比如读写磁盘文件、读写数据库、网络请求等阻塞操作,执行IO操作时,CPU处于等待状态,等待过程中操作系统会把CPU时间片分给其他线程

计算密集型任务

下面写一个计算密集型任务的例子

public class ComputeThreadPoolTest {

    final static ThreadPoolExecutor computeExecutor;

    final static List<Callable<Long>> computeTasks;

    final static int task_count = 5000;

    static {
        computeExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);

        // 创建5000个计算任务
        computeTasks = new ArrayList<>(task_count);
        for (int i = 0; i < task_count; i++) {
            computeTasks.add(new ComputeTask());
        }
    }

    static class ComputeTask implements Callable<Long> {
        // 计算一至五十万数的总和(纯计算任务)
        @Override
        public Long call() {
            long sum = 0;
            for (long i = 0; i < 50_0000; i++) {
                sum += i;
            }
            return sum;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 我电脑是四核处理器
        int processorsCount = Runtime.getRuntime().availableProcessors();
        // 逐一增加线程池的线程数
        for (int i = 1; i <=  processorsCount * 5; i++) {
            computeExecutor.setCorePoolSize(i);
            computeExecutor.setMaximumPoolSize(i);
            //直接创建所有核心线程并启动。
            computeExecutor.prestartAllCoreThreads();
            System.out.print(i);
            computeExecutor.invokeAll(computeTasks); // 预热所有线程,调用该方法会阻塞等待结果返回哦
            System.out.print("\t");
            //开始测试,测试8次
            testExecutor(computeExecutor, computeTasks);
            System.out.println();
            // 一定要让cpu休息会儿,Windows桌面操作系统不会让应用长时间霸占CPU
            // 否则Windows回收应用程序的CPU核心数将会导致测试结果不准确
            TimeUnit.SECONDS.sleep(5);// cpu rest
        }
        computeExecutor.shutdown();
    }

    private static <T> void testExecutor(ExecutorService executor, List<Callable<T>> tasks)
        throws InterruptedException {
        for (int i = 0; i < 8; i++) {
            long start = System.currentTimeMillis();
            executor.invokeAll(tasks); // ignore result
            long end = System.currentTimeMillis();
            System.out.print(end - start); // 记录时间间隔
            System.out.print("\t");
            TimeUnit.SECONDS.sleep(1); // cpu rest
        }
    }
}

将程序生成的数据粘贴到excel中,并对数据进行均值统计

注意如果相同的线程数两次执行的时间相差比较大,说明测试的结果不准确。

在这里插入图片描述

由于我笔记本的CPU有四个处理器,所以会发现当线程数达到4之后,5000个任务的执行时间并没有变得更少,基本上是在600毫秒左右徘徊。

因为计算机只有四个处理器可以使用,当创建更多线程的时候,这些线程是得不到CPU的执行的。

所以对于计算密集型任务,应该将线程数设置为CPU的处理个数,可以使用Runtime.availableProcessors方法获取可用处理器的个数。

《并发编程实战》一书中对于计算密集型任务建议线程池大小设为cpu核数+1,原因是当计算密集型线程偶尔由于页缺失故障或其他原因而暂停时,这个“额外的”线程也能确保这段时间内的CPU始终周期不会被浪费。

对于计算密集型任务,不要创建过多的线程,由于线程有执行栈等内存消耗,创建过多的线程不会加快计算速度,反而会消耗更多的内存空间;另一方面线程过多,频繁切换线程上下文也会影响线程池的性能

每个程序员都应该知道的延迟数

IO操作包括读写磁盘文件、读写数据库、网络请求等阻塞操作,执行这些操作,线程将处于等待状为了能更准确的模拟IO操作的阻塞,我觉得有必要将列举的延迟数整理出来。

参考网站:https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html

在这里插入图片描述

IO密集型任务

这里用sleep方式模拟IO阻塞:
public class IOThreadPoolTest {

    // 使用无限线程数的CacheThreadPool线程池
    static ThreadPoolExecutor cachedThreadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();

    static List<Callable<Object>> tasks;

    // 仍然是5000个任务
    static int taskNum = 5000;

    static {
        tasks = new ArrayList<>(taskNum);
        for (int i = 0; i < taskNum; i++) {
            tasks.add(Executors.callable(new IOTask()));
        }
    }

    static class IOTask implements Runnable {

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        cachedThreadPool.invokeAll(tasks);// 同样的预热线程
        testExecutor(cachedThreadPool, tasks);
        // 看看执行过程中创建了多少个线程
        int largestPoolSize = cachedThreadPool.getLargestPoolSize();
        System.out.println("largestPoolSize:" + largestPoolSize);
        cachedThreadPool.shutdown();
    }

    private static void testExecutor(ExecutorService executor, List<Callable<Object>> tasks)
        throws InterruptedException {
        long start = System.currentTimeMillis();
        executor.invokeAll(tasks);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

}

这里使用无限制的CachedThreadPool线程池,也就是说这里的5000个任务会被5000个线程同时处理,由于所有的线程都只是阻塞而不消耗CPU资源,所以5000个任务在不到2秒的时间内就执行完了。

很明显使用CachedThreadPool能有效提高IO密集型任务的吞吐量,而且由于CachedThreadPool中的线程会在空闲60秒自动回收,所以不会消耗过多的资源。

但是打开任务管理器你会发现执行任务的同时内存会飙升到接近400M,因为每个线程都消耗了一部分内存,在5000个线程创建之后,内存消耗达到了峰值。
在这里插入图片描述

所以使用CacheThreadPool的时候应该避免提交大量长时间阻塞的任务,以防止内存溢出;另一种替代方案是,使用固定大小的线程池,并给一个较大的线程数(不会内存溢出),同时为了在空闲时节省内存资源,调用allowCoreThreadTimeOut允许核心线程超时。

线程执行栈的大小可以通过-Xsssize或-XX:ThreadStackSize参数调整

混合型任务

大多数任务并不是单一的计算型或IO型,而是IO伴随计算两者混合执行的任务——即使简单的Http请求也会有请求的构造过程。

混合型任务要根据任务等待阻塞时间与CPU计算时间的比重来决定线程数量:

比如一个任务包含一次数据库读写(0.1ms),并在内存中对读取的数据进行分组过滤等操作(5μs),那么线程数应该为80左右。

线程数与阻塞比例的关系图大致如下:
在这里插入图片描述

当阻塞比例为0,也就是纯计算任务,线程数等于核心数(这里是4);阻塞比例越大,线程池的线程数应该更多。

通常我们可以按此公式算出最佳核心线程数:cpu核数✖️(1+阻塞比例) ✖️ 70% ,系统中不止一个线程池,所以实际配置线程数应该将目标CPU利用率计算进去,也就是70%。

阻塞比例 = IO耗时 / Cpu耗时 , 我们平常可以用工具apm来统计这个比例

6. 总结

线程池的大小取决于任务的类型以及系统的特性,避免“过大”和“过小”两种极端。线程池过大,大量的线程将在相对更少的CPU和有限的内存资源上竞争,这不仅影响并发性能,还会因过高的内存消耗导致OOM;线程池过小,将导致处理器得不到充分利用,降低吞吐率。

要想正确的设置线程池大小,需要了解部署的系统中有多少个CPU,多大的内存,提交的任务是计算密集型、IO密集型还是两者兼有。

虽然线程池和JDBC连接池的目的都是对稀缺资源的重复利用,但通常一个应用只需要一个JDBC连接池,而线程池通常不止一个。如果一个系统要执行不同类型的任务,并且它们的行为差异较大,那么应该考虑使用多个线程池,使每个线程池可以根据各自的任务类型以及工作负载来调整。

清山绿水始于尘,博学多识贵于勤。
我有酒,你有故事吗?
欢迎一起谈天说地,聊Java。 回复「vip课程」,获取一套价值19820 元的 java vip课程
ABA分析方法 — 软硬件开发必备的一种分析问题绝佳套路 可能你从很多地方听过这个名词,无论是在软件开发还是硬件领域(比较典型的领域:汽车、消费电子、医疗等行业),ABA验证都是一种快速分析故障的一个方法,第一次听到这个词,当时是不是一脸懵逼,本文就深度带你了解下ABA的分析方法。简要概述就是:怀疑A模块导致系统故障,将B模块替换A模块,故障消失,再将A模块替换回来,故障复现,则说明A模块是导致系统故障的原因其实,ABA分析问题,可能很多人在工作中都或多或少用过这个方法,只是没有这个概念,说明白了就是控制变量和对比验证,没什么高大上的东西。 阅读详情

相关推荐

判断线程池任务执行完成的方式

Thread线程是否执行完成,我们可以调用join方法然后等待线程执行完成;那在使用线程池的时候,我们如何知道线程已经执行完成了?本文就带给大家。

越努力越幸运。 3491

线程池使用的场景和意义

java 线程池 java 提供了四种线程池 : newCachedThreadPool newFixedThreadPool newScheduledThreadPool newSingleThreadExecutor

启迪思维,精进技术 493

A_B 测试 (A_B Testing) 原理与代码实例讲解

A/B 测试 (A/B Testing) 原理与代码实例讲解 作者:禅与计算机程序设计艺术 / Zen and the Art of Computer Programming 1. 背景介绍 1.1 问题的由来

AI天才研究院 1917

并发编程(四)线程池ThreadPoolExecutor

目录 1.线程池的作用: 2.线程池的继承类图 3.线程池的构造方法 3.1参数具体解释 4.向线程池提交任务 4.1 execute() 4.2 submit() 5.关闭线程池 6.监控· 7.线程池的简单操作 1.线程池的作用: Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个...

吾生也有涯,而学也无涯,以有涯随无涯 1167

RC电路(一):微分|积分

的取值不同,会导致输出波形和输入波形之间的关系也不同,由此也会产生不同的应用。输出为0,随着电容充满电,运放-输入端得到的分压为正最大值,可等效成一个电压源,且电压不可突变,此时电流反向为最大值,开始充电时的容抗为0,电压不可突变则电压差为0,运放。开始充电时的容抗为0,电压不可突变则电压差为0,运放。输入端得到的分压也不可突变,随着电容放完电,于是。,因电容上电压不能突变(来不及充电,相当于短路,由上分析可得积分运算电路中,输出为运放的负最大峰值,随着电容充满电,输出为运放的正最大峰值,随着电容放完电,

既有随处可栖的江湖,也有追风逐梦的骁勇! 1万+

光模块封装工艺简介

光模块按照部件可以分为芯片、光器件、光模块三个层级;按照应用领域大概可分为电信应用、数通应用两个领域;按照内部器件的封装工艺可分为TO、Box、COB三大类。 但是,无论是哪种类型的光模块,其生产工艺基本上可以分为封装、测试两大块。   那什么叫做器件封装呢?我的理解器件封装就是利用胶水、焊料、机械、热量等手段把器件按照一定的先后次序结合形成一个整体的过程,就好像早上吃的煎饼,把面粉、鸡蛋、脆饼、烤肠、葱花等这些“器件”,按照一定的顺序,通过食用油、甜面酱、加热、卷起来等手段结合到一起,形成了美味的

qq_33253182的博客 2825

一个进程 可以有多个线程池吗_灵魂发问!Java并发和线程池,只言片语真的可以讲清楚吗?...

线程池最近看到线程池,被里边乱七八槽的参数给搞晕了,你能不能给我讲讲呀?对于从事后端开发的同学来说,线程是必须要使用了,因为使用它可以提升系统的性能。但是,创建线程和销毁线程都是比较耗时的操作,频繁的创建和销毁线程会浪费很多CPU的资源。此外,如果每个任务都创建一个线程去处理,这样线程会越来越多。我们知道每个线程默认情况下占1M的内存空间,如果线程非常多,内存资源将会被耗尽。这时,我们需要线程池去...

weixin_39540744的博客 1889

逆变器专题(3)-锁相环-PLL

锁相环作为并网逆变器的第一道门户,其锁相的精度将直接影响逆变器是否并网成功,若锁相精度较差,则会直接导致系统的功率因数下降,产生无功等等。在有关文献中指出,提高PLL带宽可以准确且快速进行锁相,而当电网电压含有谐波时,可适当降低带宽抵制谐波的干扰。成为变化量,而如果两者旋转速度达到一致时,所在的交轴分量。ki很大,即带宽很大时,稳定裕度不够,锁相环锁不住。为交流量,当频率锁定而相位没有锁定时,即。,即控制环前馈,可实现将锁相速度的加快。控为0,则可实现系统完美锁相。mod,取余,可减少存储空间。

cyc19980105的博客 6183

灵魂发问!Java并发和线程池,只言片语真的可以讲清楚吗?

最近看到线程池,被里边乱七八槽的参数给搞晕了,你能不能给我讲讲呀?对于从事后端开发的同学来说,线程是必须要使用了,因为使用它可以提升系统的性能。但是,创建线程和销毁线程都是比较耗时的操作,频繁的创建和销毁线程会浪费很多CPU的资源。此外,如果每个任务都创建一个线程去处理,这样线程会越来越多。我们知道每个线程默认情况下占1M的内存空间,如果线程非常多,内存资源将会被耗尽。这时,我们需要线程池去管理线程,不会出现内存资源被耗尽的情况,也不会出现频繁创建和销毁线程的情况,因为它内部是可以复用线程的。初始化线程池后

wdj_yyds的博客 140

人形机器人电源系统硬件开发:从原理图设计到PCB布局的完整流程

人形机器人是“移动的智能终端”,其电源系统如同“心脏+血管”:既要为大脑(主控)、肌肉(电机)、感官(传感器)提供稳定能量,又要在有限体积内应对高动态负载(如跳跃时电机瞬间大电流)。本文聚焦“硬件开发”维度,覆盖从需求分析到PCB制板的完整流程,适合硬件工程师、机器人爱好者快速掌握工程实战方法。

AI天才研究院 2175

【软件系统架构】系列六: SysML(系统建模语言)

SysML(系统建模语言)是OMG组织基于UML扩展的专用建模语言,用于支持复杂系统全生命周期建模。它包含9类标准图,核心建模元素包括块(Block)、端口接口、需求和约束等。与UML相比,SysML更适用于软硬件结合的复杂系统建模。作为MBSE的核心工具,SysML通过结构图、行为图、需求图和参数图实现系统设计、需求追踪和性能验证。虽然面临学习门槛高、工具成本高等挑战,但随着AI融合和国产化替代趋势,SysML在航空航天、汽车电子等领域的应用日益广泛。SysML v2将简化语法并提升互操作性,推动系统工程

moton2017的博客 2386

关于Java线程池的灵魂8问

一、线程池基础总结 1.1 ThreadPoolExecutor线程池状态分类 RUNNABLE:运行状态,接受新任务,持续处理任务队列里的任务 SHUTDOWN:不再接受新任务,但要处理任务队列里的任务 STOP:不再接受新任务,不再处理任务队列里的任务,中断正在进行中的任务 TIDYING:表示线程池正在停止运作,中止所有任务,销毁所有工作线程 TERMINATED:表示线程池已停止运作,所有工作线程已被销毁,所有任务已被清空或执行完毕 1.2 ThreadPoolExecutor线程池状态转移图

qq_26322753的博客 763

Matlab 普通kriging(克里金)插值

Matlab 普通kriging(克里金)插值

dayuhaitang1的博客 4571

Java线程池的灵魂拷问,这些你都会吗?

问题1:为什么要使用线程池? 传统的创建线程方式 new Thread(new Runnable(){}).start(); 殊不知这种创建线程有以下缺点: 如果频繁创建和销毁线程会带来性能损耗,线程的创建和销毁都需要时间,如果创建和销毁的时间大于任务执行时间,反而得不偿失。 线程需要占用内存空间,大量的线程会抢占宝贵的内存资源,会导致oom,大量的线程也会给gc很大压力,延长gc的停顿时间。 大量的线程会抢占cpu资源,cpu在不停的做上下文切换,反而没有时间去处理线程运行的时候该处理的任务。 问题2

李林杰的博客 495

Open3D——直通滤波(python详细过程版)【2025最新版】

python就一个字:强。博客长期更新,本文最近更新时间为:2025年1月11日。

点云侠的博客 7516

OpenCASCADE:写IGES

OpenCASCADE:写IGES程序涵盖的领域流程说明初始化过程设置翻译参数执行 Open CASCADE Technology 形状转换编写 IGES 文件将开放级联技术形状映射到 IGES 实体曲线表面拓扑实体——人脸模式下的平移公差管理在 IGES 文件中设置分辨率代码架构调用图例子 程序 您可以通过以下步骤将 OCCT 形状转换为 IGES 实体: 初始化进程。 设置翻译参数, 执行模型翻译, 写入输出 IGES 文件。 您可以在写入文件之前转换多个形状。每个形状都将是 IGES 模型中的根实体。

希望我的博客,能帮上你解决学习中工作中所遇到的问题 923

java线程池灵魂8个问题

通过阻塞队列的take()方法让线程一直等待,使得Worker的run()方法一直阻塞,直到获取到执行的任务,执行完毕任务后继续阻塞等待,使得线程生命周期一直在RUNNABLE和WAITING状态之间流转,保证核心线程一直存活。1任务处理策略:线程池优先使用核心线程来处理任务,从任务添加策略可以看出,先考虑创建核心线程处理,在考虑放到阻塞队列,然后考虑创建非核心线程,达到饱和之后使用拒绝策略。4、状态管理:线程池状态和数量使用ctl表示,高3位,低29位表示线程池数量。1、线程池状态和工作线程数量的并发。

红豆和绿豆的博客 531

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

面试官:你是如何评估一个线程池需要设置多少个线程

点击上方“中间件兴趣圈”,选择“设为星标”做积极的人,越努力越幸运!见字如面,我是威哥,一个从普通二本院校毕业,从未曾接触分布式、微服务、高并发到通过技术分享实现职场蜕变,成长为Rocke...

weixin_44742132的博客 380

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

面试官:你是如何评估一个线程池需要设置多少个线程?

Java并发编程是大厂第一轮面试中的高频面试题,而线程池又是其中的典型代表,本文将梳理关于线程池的工作机制,并提出灵魂之问:你对线程池的工作机制这么了解,那你在工作中是如何判断一个线程池需...

IT技术精选文摘 459

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

浅聊一下线程池技术

用 ThreadPoolExecutor 时,按任务类型(CPU/I/O 密集)配置核心参数;执行流程:核心线程→队列→非核心线程→拒绝策略;生产环境避免用 Executors 默认线程池,自己配参数更安全;用完线程池要关闭,定期监控避免任务堆积。掌握这些,不管是日常开发还是面试,线程池这块都能应对啦~

m0_69052535的博客 994

Java线程池七大参数详解:从入门到调优实战,一文吃透核心原理

核心常设临时工,存活时间定去留,任务排队看队列,拒绝策略保底线”

全栈开发工程师 2218

动态线程池的设计和实现

动态线程池是个什么鬼,可能有些读者看到这里会感到很懵逼,甚至有些人看到这个题目,心里面立马就是一个wocao,其实,动态线程池在美团内部应用是很广泛的呢,出这个东西也是有原因的,下面我就仔细的聊一聊,聊之前呢,咱们先把线程池的一些知识复习一把。 1.线程池简介 线程池相信大家都不陌生,简单点说就是一个生产者消费者模型,生产者就是咱们自己的业务线程,不断地生产任务,消费者就是线程池中的线程,不断的消费任务。看一看下面的图就知道了。 2.线程池的核心参数 我相信大家对于创建线程池都不陌生,我们看过阿里爸爸的j

WKzhangliang123的博客 1719

Java线程池

OverrideSystem.out.println(Thread.currentThread().getName() + " 执行业务");// 自定义线程池new ArrayBlockingQueue<>(3), // 有界队列new ThreadPoolExecutor.AbortPolicy() // 默认拒绝策略try {// 模拟提交 9 个任务i < 9;i++) {前 2 个:核心线程执行接下来 3 个:进入队列。

2403_89732016的博客 925

用奶茶店的生活场景带你理解线程池相关知识点

假设你开了个奶茶店(线程池),需要招人处理订单(任务)。:人满时的应对措施——赶客、让顾客自己动手做奶茶、偷偷记下电话稍后联系等(下文细说)。:店内最多容纳10个排队顾客(任务队列),超出后触发「拒绝策略」。:防止突发流量冲垮服务器,队列满时由调用线程处理(降级)。:任务暴涨时可能创建大量线程,导致系统崩溃。:即使没顾客(任务),也得留3个员工摸鱼,:需要顺序处理任务(如日志顺序写入)。:高峰期允许再雇2人(总5人),但。:顾客E到K(共10人)陆续进店,:短时高频任务(如突发流量请求)。

2301_80447892的博客 924

31.线程池模式:高并发世界的“任务调度指挥官“

/ 自定义线程工厂@Overridereturn t;// 自定义拒绝策略@Overrideif (!try {

yezi1238的博客 917

Java面试必考点:线程池为何是高性能系统的核心秘诀?

《Java面试必考点:线程池为何是高性能系统的核心秘诀?》 线程池作为Java多线程编程的关键技术,在面试中备受关注。本文深入剖析线程池的工作原理、核心参数配置及常见应用场景,重点讲解ThreadPoolExecutor、线程池类型(Fixed/Cached/Scheduled/Single)以及拒绝策略等知识点。通过掌握这些内容,开发者能够更好地优...

yanpeng2580的博客 608
上一篇: 为什么牛逼的程序员都不用 “ ! = null “ 做判空?
下一篇: 女朋友也能看懂的Zookeeper分布式锁原理
yi博说
博客等级 码龄11年 20粉丝 30原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值