线程状态聊聊

聊聊JVM(五)从JVM角度理解线程 这篇说说如何从JVM的角度来理解线程,可以对Java的线程模型有一个更加深入的理解,对GC的一些细节也会理解地更加深刻。本文基于HotSpot的OpenJDK7实现。 我们知道JVM主要是用C++实现的,JVM定义的Thread的类继承结构如下: Class hierarchy  - Thread    - NamedThread      - VMThread      - 阅读详情

线程状态

线程状态,这个问题好像很容易,但也容易模糊,而且有好几个版本,我就以我自认为对的版本,记录下。

线程6状态

在这里插入图片描述

  1. 新建
    当一个线程对象被创建,但还未调用 start 方法时处于新建状态
    此时未与操作系统底层线程关联
  2. 可运行
    调用了 start 方法,就会由新建进入可运行
    此时与底层线程关联,由操作系统调度执行
  3. 终结
    线程内代码已经执行完毕,由可运行进入终结
    此时会取消与底层线程关联
  4. 阻塞
    当获取锁失败后,由可运行进入 Monitor 的阻塞队列阻塞,此时不占用 cpu 时间
    当持锁线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行状态
  5. 等待
    当获取锁成功后,但由于条件不满足,调用了 wait() 方法,此时从可运行状态释放锁进入 Monitor 等待集合等待,同样不占用 cpu 时间
    当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的等待线程,恢复为可运行状态
  6. 有时限等待
    当获取锁成功后,但由于条件不满足,调用了 wait(long) 方法,此时从可运行状态释放锁进入 Monitor 等待集合进行有时限等待,同样不占用 cpu 时间
    当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁
    如果等待超时,也会从有时限等待状态恢复为可运行状态,并重新去竞争锁
    还有一种情况是调用 sleep(long) 方法也会从可运行状态进入有时限等待状态,但与 Monitor 无关,不需要主动唤醒,超时时间到自然恢复为可运行状态

线程疑问

  • 什么是上述的等待集合
  • 什么是阻塞集合
  • notity和notifyall唤醒的线程,有顺序吗

等待集合,阻塞集合

对于同一个对象上锁,只有先拿到这个锁的线程先进入到代码中,其他的线程,再在这个对象的阻塞队列中等待,这就是一种阻塞状态;
针对线程中执行wait方法,就会将线程放入到等待队列中,一旦调用notifyall方法,也就会将所有等待队列中对象,放入到阻塞队列中进行排队等待。
在这里插入图片描述

在这里插入图片描述
举例代码测试
1、测试第一个是否有对于同一把锁是否有执行顺序

public class TestSynchronized {
    static List<Thread> list = new ArrayList<>();
    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(() -> {
                synchronized (lock) {
                    System.out.println(String.format("{%s}-thread executed",Thread.currentThread().getName()));
                    try {
                        //这里的睡眠没有什么意义,仅仅为了控制台打印的时候有个间隔 视觉效果好
                        TimeUnit.MILLISECONDS.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t" + i);//给每个线程去了一个名字 t1 t2 t3 ....

            list.add(t);
        }

        System.out.println("---启动顺序 调度顺序或者说获取锁的顺序讲道理是正序0--9----");

        synchronized (lock) {
            for (Thread thread : list) {
                //这个打印主要是为了看到线程启动的顺序
                System.out.println(String.format("{%s}-启动顺序--正序0-9", thread.getName()));
                thread.start();//  CPU 调度?

                //间隔时间表示启动有先后顺序,保证加入到等待队列中
                //虽然我们的启动顺序是正序的(t1--t2),但是调度顺序是错乱的  t2---t1
                TimeUnit.MILLISECONDS.sleep(1);
            }
            System.out.println("-------执行顺序--正序9-0");
        }
    }
}

结果:放入线程是t0-t9,但是执行时是t9-t0,结合我上面画的图理解

2、测试第二个阻塞队列notifyAll之后的执行顺序

public class TestWaitOrder {

    static  Object object = new Object();//锁对象

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {//jack
            synchronized (object){
                System.out.println(String.format("{%s}-没有香烟 我去等待老板安排 先休息,安排好之后叫醒我",Thread.currentThread().getName()));
                try {
                    //jack线程进入阻塞,但是释放了锁
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("{%s}-开始工作,大中华真不错",Thread.currentThread().getName()));
            }
        }, "jack").start();
        TimeUnit.SECONDS.sleep(1);

        new Thread(() -> {//jack
            synchronized (object){
                System.out.println(String.format("{%s}-没有香烟 我去等待老板安排 先休息,安排好之后叫醒我",Thread.currentThread().getName()));
                try {
                    //jack线程进入阻塞,但是释放了锁
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("{%s}-开始工作,大中华真不错",Thread.currentThread().getName()));
            }
        }, "tom").start();
        TimeUnit.SECONDS.sleep(1);

        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            synchronized (object){
                //log.debug("没有加钱,先休息不干活");
                System.out.println(String.format("{%s}-没有加钱,先休息不干活",Thread.currentThread().getName()));
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("{%s}-干活了----有钱能使鬼推",Thread.currentThread().getName()));
            }
        }, "rose").start();

        //5s之后叫醒jack
        TimeUnit.SECONDS.sleep(5);
        new Thread(() -> {//jack
            synchronized (object){
                System.out.println("买了两包大中华,给加钱了");
                object.notifyAll();
            }
        }, "boss").start();

    }
}

结果发现:放入顺序是jeck-tom-rose,叫醒之后的顺序是rose-tom-jeck,结合画的图理解

3、notify到底叫醒的是哪个线程,有规律吗?

将上述代码进行改造,只需将notifyAll变成notify,多测试几次,并且把上述线程(jack,tom,rose)顺序进行调整。

结果发现:调用notity之后,叫醒的第一个线程是最先放入等待队列中的(最早执行wait),这和notifyAll矛盾吗?其实不矛盾,可以理解为notify只是将第一个移到阻塞队列中了,nitifyAll是把几个都放到了阻塞队列中了,因为阻塞队列执行是从尾部弹出的,所以就能理解了。

4、等待队列中notify的线程会比阻塞队列的先执行吗?

判断等待队列中唤醒的是否插入到了阻塞队列的尾部,进行代码测试

public class TestWaitAndBlockOrder {

    static  Object object = new Object();//锁对象

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {//jack
            synchronized (object){
                System.out.println(String.format("{%s}-没有中华 我去等待老板安排 先休息,安排好之后叫醒我",Thread.currentThread().getName()));
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("{%s}-开始工作",Thread.currentThread().getName()));
            }
        }, "jack").start();


        TimeUnit.SECONDS.sleep(1);

        System.out.println("1s之后主线程获取锁");
        System.out.println("因为jack wait释放了锁,主线程能够获取到");
        System.out.println("-----------关键看打印顺序----------------");
        synchronized (object) {

            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    synchronized (object) {
                        System.out.println(String.format("{%S}-那些默默无闻的普通人",Thread.currentThread().getName()));
                    }
                }, "t" + i).start();
                TimeUnit.MILLISECONDS.sleep(1);
            }

			//先唤醒jeck
            object.notifyAll();
        }

    }
}

这不能说明等待的线程加入到阻塞队列的尾部,也可能是巧合,那就调换下object.notifyAll()的位置,把这个代码移到普通线程前面

public class TestWaitAndBlockOrder {

    static  Object object = new Object();//锁对象

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {//jack
            synchronized (object){
                System.out.println(String.format("{%s}-没有中华 我去等待老板安排 先休息,安排好之后叫醒我",Thread.currentThread().getName()));
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("{%s}-开始工作",Thread.currentThread().getName()));
            }
        }, "jack").start();


        TimeUnit.SECONDS.sleep(1);

        System.out.println("1s之后主线程获取锁");
        System.out.println("因为jack wait释放了锁,主线程能够获取到");
        System.out.println("-----------关键看打印顺序----------------");
        synchronized (object) {
            //先唤醒jeck
            object.notifyAll();
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    synchronized (object) {
                        System.out.println(String.format("{%S}-那些默默无闻的普通人",Thread.currentThread().getName()));
                    }
                }, "t" + i).start();
                TimeUnit.MILLISECONDS.sleep(1);
            }

            //后唤醒杰克
           // object.notifyAll();
        }

    }
}

结果发现:等待队列中的线程唤醒后加入阻塞队列的尾部。

总结

block状态的线程,是有个阻塞的队列中存放的,而且是尾部先执行的意思;
wait状态的线程,有个等待队列中存放的,唤醒之后,加入阻塞队列的尾部;
wait状态先变成block状态,再进行执行,我是这么理解的,先姑且这么理解

学习大佬博客:并发编程系列——wait原理的讨论(1)

我只是知识进行理解记忆,学习请参考大佬博客

原来还能这样看Java线程的状态及转换 现今主流的JVM,比如hotspot虚拟机都是把Java 线程,映射到操作系统OS底层的线程上,把调度委托给了操作系统。在Java线程的时间中,关于线程的一切的起点是从Thread 类的对象的创建开始,一般实现Runnable接口 或者 继承Thread类的类,实例化一个对象出来,线程就进入了初始状态。大家好,我是呼噜噜,博主还没羊,最近一直在梳理Java并发,但内容杂且偏晦涩,今天我们一起来聊聊Java 线程的状态及转换 先来夯实一下基础,万丈高楼平地起,路还是得慢慢走。如果竞争到锁,就直接接着运行。 阅读详情

相关推荐

Ubuntu安装ToDesk远程控制软件

前几天有同学问我怎么在 Ubuntu 系统上安装 ToDesk 远程控制软件, 帮他安装完成后就 顺手整理了一下安装过程,所以就顺手有了这篇在 Ubuntu 系统上安装 todesk 的文章。

小白的博客 8660

线程池及线程状态

线程池的两种创建方式及线程的六种状态,sleep,wait,join的使用

Peng_zhj的博客 3027

火山引擎之火山方舟知识库+OpenWebUI,搭建独属于自己的AI智能客服

创建接入点,接入点名称可按个人需求而定,比如Doubao-1.5-pro-32k,在添加模型选择Doubao-1.5-pro-32k,点击确定(PS:有些模型不支持知识库,若发现后续无法选择,请重新创建推理接入点。选择应用广场,创建零代码应用,在创建好的应用设置内编辑相关信息,并选择推理接入点,即先前建好的推理接入点;此处的模型名称是火山的模型ID名称,可在OpenWebUI设置-模型 中修改名称。至此所需的apikey和模型ID和请求的api地址(可选配置,非本文重点参数)。进入火山官网,注册账号。

AmaZInGvCH的博客 2654

线程及线程池的五种状态

线程的5种状态 线程可以有如下5种状态:New 、Runnable 、Running 、Blocked 、Dead 状态之间的转换如图: 1.New (新创建) 当用new操作符创建一个线程时,如new Thread®,该线程还没有开始运行。这意外这它的状态是new。此时程序还没有开始运行线程中的代码,在线程运行之前还有一些基础工作要做。 2.Runnable (可运行/就绪) 一个新创建的线程...

qq_24384579的博客 1万+

抖音无人直播技术全解析:从原理、实操到合规化思考

直播技术作为内容分发的核心形式,其底层依赖于流媒体协议(如RTMP)与实时音视频编码推流。通过OBS等软件进行场景合成与虚拟摄像头技术,可以实现丰富的画面包装与自动化播放。这项技术的核心价值在于突破人力与时间的限制,实现内容的持续输出与流量获取,在电商带货、品牌展示、陪伴式内容等场景中具有应用潜力。然而,单纯依赖录播循环存在高风险,平台风控系统会通过内容重复性检测与互动行为模型进行识别。因此,更可持续的方向是探索“半无人直播”或自动化辅助工具,例如利用AI数字人驱动或直播切片技术,在合规前提下提升运营效率。

weixin_29895109的博客 509

解决usbisp不识别无法烧录Atmega328P,Arduino不识别问题

如果使用Arduino IDE里面的烧录,可能会遇到一个问题,就是不识别国产这种廉价的USBISP 点击烧录引导程序,会遭遇到找不到USB设备的问题,选哪个都会提示找不到,比如选择USBasp could not find USB device with vid=0x16c0 pid=0x5dc vendor='www.fischl.de' product='USBasp' 因此,我们需要使用到progisp这一个软件的1.72版本 选择好芯片的型号后,调用编译好的HEX文...

arenascat的博客 7441

线程状态与线程池

线程状态 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,在API中,java.lang.Thread.State这个枚举中给出了六种线程状态: 1.NEW(新建) 线程刚被创建,但是并未启动,还没调用start方法 2。Runnable(可运行) 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,...

qq_44824292的博客 357

LM Studio 模型加载与转换实战指南:从GGUF格式到本地部署

本文提供了使用LM Studio加载与转换GGUF格式大语言模型的完整实战指南。详细阐述了为何GGUF格式是本地部署的最佳选择,并逐步指导用户如何将非GGUF模型(如Transformers格式)转换为GGUF格式,以及在LM Studio中进行加载、配置GPU卸载等关键优化,最终实现流畅的本地离线对话体验。

weixin_29061425的博客 922

线程池的五种状态和线程的6种状态

线程池的五种状态 running,showdown,stop,Tiding,Terminated runing: 线程池处于运行状态,可以接受任务,执行任务,创建线程默认就是这个状态。 showDown 调用showdown()函数,不会接受新任务,但是会慢慢处理完堆积的任务。 Stop 调用showdownnow()函数,不会接受新任务,不处理已有任务,会中断现有任务。 Tiding 当线程池状态为showdown或者stop,任务数量为0,就会变为tidying。这个时候会调用钩子函数termina

evanYang的博客 1278

USB3.0/3.1信号完整性分析仿真

• 1.了解USB3.0通路的互连关系,解读原理图接口电路和PCB的互连电路。对USB布线的通路及阻抗进行检查和分析。 • 2.使用USB3.0实例文件提取互连线的S参数,选择SSRX+,SSRX-,SSTX+,SSTX-,D+,D-组的互连线,使用PowerSI,Clarity,3DFEM等工具提起关键网络的S参数。提取S参数以后需要分析S参数的互连特征,对S参数的结果开展评价。 • 3.使用SYSTEM SI USB3 Compliance工程向导模型,建立USB仿真实例框架,在框架BOLCK中带入S参

小猪配齐电子设计 1万+

倍福EL6002实现RS232自由口通信,以称重模块RS232数据接收为例

RS-232是美国电子工业联盟制定的串行数据通信接口标准,原始编号全称是EIA-RS-232(简称RS232),它被广泛用于DCE(Data Communication Equipment ) 和 DTE(Data Terminal Equipment) 之间的连接。最早的台式电脑都会保留9针的232接口,用于串口通信,目前基本被USB接口取代。CTS是对请求发送RTS信号的响应,当DCE已经准备好接收数据时,该信号有效(ON),通知DTE可用使用TXD发送数据了。弄完了,再将CTS设置为1,让A发数据。

weixin_41883890的博客 1726

【Java并发】聊聊线程的几种状态

【Java并发】聊聊线程的几种状态

芝兰生于深谷,不以无人而不芳 668

YOLO26模型应用过程中的报错处理及疑问解答,涉及环境搭建、模型训练、模块改进、论文写作

本文记录了YOLO26模型的这一系列过程中可能发生的问题,以及大家非常关心的问题,这里统一做了一个记录和解答。

Limiiiing的博客 241

聊聊Java线程状态和常见误区

一、Java Thread文档 关于Java的线程状态,[Java Thread文档]讲的非常的清楚了,如果英文水平有4级的话,建议直接阅读文档。(https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.State.html) 二、Java线程状态 java的线程一共有6个状态:New,Runnable,Waiting,Timed_Waiting,Blocked,Terminate。 这里Java使用Thread类的内部枚举类进行表示,我删除文档

wujingyicode的博客 350

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

一起聊聊内核中的线程:操作函数、进程状态、task_struct、举个例子、

**内核线程是直接由内核本身启动的进程**。 内核线程实际上**是将内核函数委托给独立的进程**,它与内核中的其他进程”并行”执行。 **内核线程经常被称之为内核守护进程**。内核线程是被调度的实体,它**被加入到某种数据结构中**,调度程序根据实际情况进行线程的调度。 **内核线程与用户态线程的作用类似**,通常用于**执行某些周期性的计算任务,或者在后台执行需要大量计算的任务**。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 462

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

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

聊聊线程池中哪几种状态,分别表示什么?8 年 Java 开发:从业务踩坑到源码拆解(附监控实战)

服务优雅停机时,调用`shutdown()`后还在提交任务,结果被拒绝; - 线程池明明有空闲线程,却不处理队列任务,查了半天才发现是处于`STOP`状态; - 监控面板上 “线程池活跃数” 一直不变,最后定位到线程池已悄悄进入`TERMINATED`状态。

Minoz_wl的博客 862

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

UDP群聊聊天室:Linux网络编程实战与性能优化

UDP协议作为传输层核心协议之一,以其无连接和低延迟特性在实时通信领域占据重要地位。与TCP不同,UDP不需要维护连接状态,数据包独立传输,特别适合局域网环境下的群聊应用。通过Socket编程实现UDP通信时,合理设置SO_REUSEADDR等选项能显著提升服务稳定性。在工程实践中,UDP群聊系统通常采用C/S架构,结合广播/组播技术实现高效消息分发,配合轻量级心跳机制维护客户端状态。针对性能优化,调整UDP缓冲区大小、采用多线程处理模型是关键手段。这类技术在视频会议、在线游戏等低延迟场景中具有广泛应用,本

weixin_31236101的博客 308

敞开心扉,一起聊聊Java多线程

目录敞开心扉,一起聊聊Java多线程(结尾有福利~)一、线程的实现方式1.继承Thread类,重写run方法2.实现Runnable接口,重写run方法3.实现Callable接口重写run方法,通过FutureTask包装器获取返回值二、线程的生命周期三、线程状态转换四、线程的启动经典面试题源码剖析五、线程的终止1.暴力终止法:stop()2.自定义标志位终止3.优雅中断法:interrupt()六、线程的复位1.Thread.interrupted()2.通过抛出InterruptedException

猿医ˉ生 3858

多线程——线程状态

本文主要介绍Java线程的六种状态及其转换关系:1)NEW(新建未启动);2)RUNNABLE(可运行/执行中);3)BLOCKED(锁阻塞);4)WAITING(无限期等待);5)TIMED_WAITING(限时等待);6)TERMINATED(终止状态)。

2301_76937176的博客 710

你的接口真的线程安全了么?聊聊保证线程安全的10个小技巧

作者是名退役复学在校大学生,对jdk、spring、springboot、springcloud、mybatis等开源框架源码有一定研究,欢迎关注,和我一起交流。对于从事后端开发的同学来说,问题是我们每天都需要考虑的问题。线程安全问题通俗的讲:主要是在多线程的环境下,不同线程同时读和写公共资源(临界资源),导致的数据异常问题。比如:变量a=0,线程1给该变量+1,线程2也给该变量+1。此时,线程3获取a的值有可能不是2,而是1。线程3这不就获取了错误的数据?线程安全问题会直接导致数据异常,从而影响业务功能的

退役复学在校大学生,目光所至,皆为华夏 5935

聊聊线程池

前提 案例分析:用户购买商品成功后,需要调用订单系统等系统来生成数据,如果主程序逐行等待其他系统成功后再通知客户,用户购买响应时间是十分长的,这个是不能容忍。细细看来,购买操作和生成操作可以说是两个相互独立的步骤,为了提高程序执行效率,生成数据操作我们可以构建出来一个线程来进行通知,主线程执行完后就直接把结果通知给客户,这就叫做软件开发中的分而治之。但此时购买人特别多的时候,意味着我们需要构建...

山东军团 228

聊聊线程与线程池

文章目录原文介绍线程状态构造方法拒绝策略线程池的执行流程常见线程池测试代码 原文介绍 原文链接 这篇文章介绍了线程与线程池。 线程状态 引用原文 第一步,是用new Thread()的方法新建一个线程,在线程创建完成之后,线程就进入了就绪(Runnable)状态,此时创建出来的线程进入抢占CPU资源的状态,当线程抢到了CPU的执行权之后,线程就进入了运行状态(Running),当该线程的任务执行完成之后或者是非常态的调用的stop()方法之后,线程就进入了死亡状态。 而我们在图解中可以看出,线程还具有一

HelloiChen的博客 548

聊聊 ThreadPoolExecutor 线程池及其源码

在我们的实际开发中,通常使用线程池创建线程资源。而线程池本质上是一种池化技术,利用资源复用的思想,复用线程资源。

weixin_74412978的博客 1190

java线程的状态有几种_java线程有哪几种几种状态

java线程有哪几种几种状态发布时间:2020-07-24 16:16:22来源:亿速云阅读:88作者:Leah今天就跟大家聊聊有关java线程有哪几种几种状态,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。java线程的5种状态:1、初始状态。2、可运行状态,位于可运行线程池中,等待被线程调度选中,获取cpu的使用权。3、运行状态。4、阻塞...

weixin_34911236的博客 770

聊聊保证线程安全的10个小技巧

​__ ​前言: 作者是名退役复学在校大学生,对jdk、spring、springboot、springcloud、mybatis等开源框架源码有一定研究,欢迎关注,和我一起交流。对于从事后端开发的同学来说,线程安全问题是我们每天都需要考虑的问题。线程安全问题通俗的讲:主要是在多线程的环境下,不同线程同时读和写公共资源(临界资源),导致的数据异常问题。比如:变量a=0,线程1给该变量+1,线程2也给该变量+1。此时,线程3获取a的值有可能不是2,而是1。线程3这不就获取了错误的数据?

ZL_1618的博客 1480
上一篇: 谷粒商城收获记录2
下一篇: 转载--32个鲜为人知的自学网站
sun_code
博客等级 码龄12年 6粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值