《java学习笔记》之多线程初步,java面试数据结构与算法高频考

myThread3.setName(“Thread1”);

//启动线程

myThread3.start();

//main线程

for (int i = 0; i < 100; i++) {

System.out.println(Thread.currentThread().getName() + " —> " + i);

}

}

}

class MyThread3 extends Thread{

public void run(){

//获取当前线程的名字

System.out.println(currentThread().getName());

for (int i = 0; i < 100; i++) {

System.out.println(currentThread().getName() + " —> " + i);

}

}

}

(二)

/*

  • 关于线程的sleep()方法

  • 1.静态方法

  • 2.参数是毫秒

  • 3.作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用

  •   这行代码出现在A线程中,A线程就会进入休眠。
    
  •   这行代码出现在B线程中,B线程就会进入休眠。
    
  • 4.Thread.sleep()方法,可以做到这样的效果:

  •   间隔特定的时间,去执行一段特定的代码,每隔多久执行一次
    
  • */

public class ThreadTest05 {

public static void main(String[] args) {

/*//在main进程中,所以使用sleep方法,是使得main方法进入阻塞状态

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

//五秒后执行此代码

System.out.println(“Hello World”);

*/

//使得每个一秒钟输出一个数字

for (int i = 0; i < 10; i++) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(i);

}

}

}

/*

  • Thread.sleep()方法的面试题

  • */

public class ThreadTest06 {

public static void main(String[] args) {

//创建线程对象

MyThread5 myThread = new MyThread5();

myThread.setName(“ttt”);

myThread.start();

//请问这会使得 myThread 休眠一秒吗

try {

myThread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//不会,因为sleep是一个静态方法,最终在编译器会看做Thread.sleep() 这个的作用是休眠当前线程

//当前线程是main线程

System.out.println(“Hello World”);

//Hello World

//0

//1

//2

//3

//4

}

}

class MyThread5 extends Thread{

@Override

public void run() {

for (int i = 0; i < 5; i++) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(i);

}

}

}

(三)

/*

  • 怎么唤醒正在休眠的线程?

  •   不是终断线程的执行,是终止线程的睡眠
    
  • */

public class ThreadTest07 {

public static void main(String[] args) {

//创建线程对象

Thread myThread6 =new Thread(new MyThread6());

myThread6.setName(“钢铁侠 Thread”);

myThread6.start();

//希望过5秒能让 myThread6 醒过来

//一年太长了

try {

//当前线程是主线程

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

//这个方法是利用了异常处理

终止t线程的睡眠

myThread6.interrupt();

}

}

class MyThread6 implements Runnable{

@Override

public void run() {

//休眠一年

System.out.println(Thread.currentThread().getName() + " —> begin");

try {

Thread.sleep(365 * 24 * 60 * 60 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//一年之后再执行

System.out.println(Thread.currentThread().getName() + “—> over”);

}

}

(四)

//在java中怎么强行终止一个线程的执行

public class ThreadTest08 {

public static void main(String[] args) {

MyThread7 myThread7 =new MyThread7();

myThread7.setName(“美国队长 Thread”);

myThread7.start();

//过五秒后让 myThread7 终止

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

myThread7.stop();//已过时,不建议使用

}

//stop存在缺点,容易丢失数据,因为直接将线程杀死了

//美国队长 Thread --> 0

//美国队长 Thread --> 1

//美国队长 Thread --> 2

//美国队长 Thread --> 3

//美国队长 Thread --> 4

}

class MyThread7 extends Thread{

public void run(){

for (int i = 0; i < 100; i++) {

//每个一秒输出一个数字

System.out.println(Thread.currentThread().getName() + " --> " + i);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

(五)

/*

  • 怎么合理的终止一个线程的执行?

  • */

public class ThreadTest09 {

public static void main(String[] args) {

MyThread8 myThread =new MyThread8();

Thread myThread8 = new Thread(myThread);

myThread8.setName(“黑豹”);

myThread8.start();

//模拟五秒

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

//想什么时候关闭,什么时候讲run 变成false就行了

myThread.run = false;

}

}

class MyThread8 implements Runnable{

//布尔标记

boolean run = true;

@Override

public void run() {

for (int i = 0; i < 100; i++) {

if (run){

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(i);

}else{

//有什么要保存的在这保存

return;

}

}

}

}

(六)

//了解:关于线程的优先级

public class ThreadTest010 {

public static void main(String[] args) {

//线程的优先级

System.out.println(Thread.MAX_PRIORITY);//10

System.out.println(Thread.NORM_PRIORITY);//5

System.out.println(Thread.MIN_PRIORITY);//1

//将main线程的优先级变为1

Thread.currentThread().setPriority(1);

Thread myThread9 = new Thread(new MyThread9());

myThread9.setName(“蜘蛛侠”);

优先级较高的,只是抢到的CPU时间片相对多一些

myThread9.start();

for (int i = 0; i < 1000; i++) {

System.out.println(“main —>” + i);

}

}

}

class MyThread9 implements Runnable{

@Override

public void run() {

System.out.println("线程的默认优先级是 " + Thread.currentThread().getPriority());

for (int i = 0; i < 1000; i++) {

System.out.println(Thread.currentThread().getName() + “—>” + i);

}

}

}

(七)

/*

  • 让位:

*当前线程暂停,回到就绪转态,让给其它线程

  • static void yield() 静态方法*/

public class ThreadTest11 {

public static void main(String[] args) {

Thread myThread = new MyThread10();

myThread.setName(“雷神”);

myThread.start();

for (int i = 0; i < 1000; i++) {

System.out.println("main —> " +i);

}

}

}

class MyThread10 extends Thread{

public void run(){

for (int i = 0; i < 1000; i++) {

//每输出100就让位一次

if (i %100 == 0){

Thread.yield();//当前线程暂停一下,让给主线程

}

System.out.println(Thread.currentThread().getName() + "—> " + i);

}

}

}

(八)

/*

  • 线程的合并

  • */

public class ThreadTest12 {

public static void main(String[] args) {

System.out.println(“main begin!!!”);

Thread myThread11 = new MyThread11();

myThread11.setName(“黑寡妇”);

myThread11.start();

try {

//不是栈的合并,是栈之间的协调

myThread11.join();//myThread11 合并到 主线程,主线程进入阻塞状态,当 myThread11 执行完再执行

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“main over”);

}

//main begin!!!

//0

//1

//2

//3

//4

//main over

}

class MyThread11 extends Thread{

@Override

public void run() {

for (int i = 0; i <5 ; i++) {

System.out.println(i);

}

}

}

五.线程安全


(一)模拟银行操作系统

public class Test {

public static void main(String[] args) {

//创建银行账户

Account account =new Account(“美国队长的账户”,10000);

//创建两个线程

//猎鹰和美国队长同时去取钱

AccountThread accountThread1 = new AccountThread(account);

AccountThread accountThread2 = new AccountThread(account);

accountThread1.setName(“美国队长”);

accountThread2.setName(“猎鹰”);

//取款

accountThread1.start();

accountThread2.start();

//猎鹰 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//美国队长 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//这就出错了

}

}

public class AccountThread extends Thread{

//同一个进程有相同的账号对象

//模拟两个人同时去ATM机器取钱

//has a

private Account account;

//构造方法,传入银行账户属性

public AccountThread(Account account) {

this.account = account;

}

@Override

//run方法的执行表示取款操作

public void run() {

//假设就取5000元钱

double money = 5000;

account.withdraw(money);

//取款信息

System.out.println(Thread.currentThread().getName() + " 对 " + account.toString() + " 取款了" + money + "元,余额是 " + account.getBalance());

}

}

//银行账户类

public class Account {

//账号

private String acton;

//余额

private double balance;

//构造方法

public Account() {

}

public Account(String acton, double balance) {

this.acton = acton;

this.balance = balance;

}

//set和get方法

public String getActon() {

return acton;

}

public void setActon(String acton) {

this.acton = acton;

}

public double getBalance() {

return balance;

}

public void setBalance(double balance) {

this.balance = balance;

}

//取款方法

public void withdraw(double money){

//取前余额

double before = this.balance;

//取后余额

double after = before - money;

//模拟网络延迟,就一定会出错

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//更新余额

setBalance(after);

}

@Override

public String toString() {

return acton;

}

}

(二)synchronized第一种使用方式

public class Test {

public static void main(String[] args) {

//创建银行账户

Account account =new Account(“美国队长的账户”,10000);

//创建两个线程

//猎鹰和美国队长同时去取钱

AccountThread accountThread1 = new AccountThread(account);

AccountThread accountThread2 = new AccountThread(account);

accountThread1.setName(“美国队长”);

accountThread2.setName(“猎鹰”);

//取款

accountThread1.start();

accountThread2.start();

//猎鹰 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//美国队长 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//这就出错了

}

}

public class AccountThread extends Thread{

//同一个进程有相同的账号对象

//模拟两个人同时去ATM机器取钱

//has a

private Account account;

//构造方法,传入银行账户属性

public AccountThread(Account account) {

this.account = account;

}

@Override

//run方法的执行表示取款操作

public void run() {

//假设就取5000元钱

double money = 5000;

account.withdraw(money);

//取款信息

System.out.println(Thread.currentThread().getName() + " 对 " + account.toString() + " 取款了" + money + "元,余额是 " + account.getBalance());

}

}

(三)synchronized第二种使用方式

//银行账户类

public class Account {

//账号

private String acton;

//余额

private double balance;

//构造方法

public Account() {

}

public Account(String acton, double balance) {

this.acton = acton;

this.balance = balance;

}

//set和get方法

public String getActon() {

return acton;

}

public void setActon(String acton) {

this.acton = acton;

}

public double getBalance() {

return balance;

}

public void setBalance(double balance) {

this.balance = balance;

}

//取款方法

public void withdraw(double money){

//以下的代码必须是排队的,要不然就可能会出问题

//一个线程把这里的代码全部执行完之后,另一个线程才能进来

/*

  • 线程同步代码的语法

  • synchronized(){

}

  • synchronized 是一个关键字

  • 后面的小括号填的东西非常重要,填的是你想要同步的线程的数据

  • 这个数据必须是多线程的共享数据,才能达到多线程的排队

  • ()里面写什么?

  •   那看你想让哪些线程同步?
    
  •   假设t1,t2,t3,t4,t5,有五个线程
    
  •   你只希望t1,t2,t3排队,t4,t5不需要排队,怎么办?
    
  •   你就在()中写一个t1,t3,t3共享的对象,而这个对象对于t4 t5来说是不共享的
    
  • */

/*

  • 对于这里来说,银行的账户是共享的,就可以填银行的账户

  • withdraw 是一个实例方法,所以this 就是银行对象

  • 在java中,每一个对象都有一个对象锁

  • 多少个对象,多少把锁

  • 以下是线程同步的原理:

  • t1和t2两个线程:

  •   1. 假设t1和t2线程是并发的,那么肯定一先一后执行下面的代码
    
  •       假设t1先执行到这个代码,t1看到 synchronized 关键字就会去找后面的“后面共享对象”的对象锁,
    
  •       找到之后就会占有这把锁,然后执行同步代码块的代码,在程序中一直占有这把锁,直到t1执行完毕,才会释放这个
    
  •       “共享对象”的对象锁
    
  •   2.假设t1已经占有了这把锁,t2这时候进到了synchronized中,去寻找对象锁,找到了对象锁,但是发现对象锁被t1占有了
    
  •       那么也只能停下来,等到t1之星完毕,释放对象锁,才可以执行同步代码块
    
  • 这样就达到了线程排队执行

  •   这里要注意的是:这个共享对象一定要选好,
    
  •   这个共享对象 是需要排队执行的这些线程对象所共享的
    
  • */

//synchronized (object){ //也可以

//synchronized (“abc”){ //"abc"在字符串常量池当中,所有的线程都会等待

synchronized (this){

//取前余额

double before = this.balance;

//取后余额

double after = before - money;

//即使有网络延迟也没关系了

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//更新余额

setBalance(after);

}

}

@Override

public String toString() {

return acton;

}

}

package caopeng.javaseTest.threadsafetest.treadsafe3;

//银行账户类

public class Account {

//账号

private String acton;

//余额

private double balance;

//构造方法

public Account() {

}

public Account(String acton, double balance) {

this.acton = acton;

this.balance = balance;

}

//set和get方法

public String getActon() {

return acton;

}

public void setActon(String acton) {

this.acton = acton;

}

public double getBalance() {

return balance;

}

public void setBalance(double balance) {

this.balance = balance;

}

//取款方法

//在方法的声明上使用synchronized,那么锁住一定是 this

//方法体就是同步代码块,可能会扩大同步的范围

//如果共享的就是this,并且需要同步的就是整个方法体,建议使用这个

//缺点:不够灵活

//优点:代码整洁

public synchronized void withdraw(double money){

//取前余额

double before = this.balance;

//取后余额

double after = before - money;

//模拟网络延迟,就一定会出错

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//更新余额

setBalance(after);

}

@Override

public String toString() {

return acton;

}

}

(四)synchronized第三种使用方式

在静态方法上用 synchronized 修饰,这个时候占用的是类锁,一个类一个锁,这个类的所有对象占有这一把锁

在下面的题目中最后一题有具体的代码实现

六.简单的Synchronized题目


(一)题目一

//问 doOther需要等 dosome 结束吗

public class Exam {

public static void main(String[] args) {

//创建一个myClass对象

MyClass myClass =new MyClass();

//创建两个线程对象

Thread thread1 = new MyThread(myClass);

Thread thread2 = new MyThread(myClass);

thread1.setName(“钢铁侠”);

thread2.setName(“美国队长”);

//启动线程

thread1.start();

try {

Thread.sleep(1000);//确保 钢铁侠 线程先执行

} catch (InterruptedException e) {

e.printStackTrace();

}

thread2.start();

}

//dosome begin!!!

//doOther begin!!!

//doOther over!!!

//dosome over!!!

//不需要,因为doOther没有synchronized修饰,不需要对象锁,即使 myclass 对象的对象锁的确被 钢铁侠线程占有了

}

class MyThread extends Thread{

MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

(二)题目二

//问 doOther需要等 dosome 结束吗

public class Exam {

public static void main(String[] args) {

//创建一个myClass对象

MyClass myClass =new MyClass();

//创建两个线程对象

Thread thread1 = new MyThread(myClass);

Thread thread2 = new MyThread(myClass);

thread1.setName(“钢铁侠”);

thread2.setName(“美国队长”);

//启动线程

thread1.start();

try {

Thread.sleep(1000);//确保 钢铁侠 线程先执行

} catch (InterruptedException e) {

e.printStackTrace();

}

thread2.start();

}

//dosome begin!!!

//dosome over!!!

//doOther begin!!!

//doOther over!!!

//需要,因为 doOther此时也有Synchronized修饰,需要对象锁,但是doSome方法先执行,抢占了对象锁

//所以只能等 dosome方法执行完之后才能占有对象锁,然后执行同步代码块

}

class MyThread extends Thread{

MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public synchronized void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

(三)题目三

//问 doOther需要等 dosome 结束吗

public class Exam {

public static void main(String[] args) {

//创建两个myClass对象

MyClass myClass1 =new MyClass();

MyClass myClass2 =new MyClass();

//创建两个线程对象

Thread thread1 = new MyThread(myClass1);

Thread thread2 = new MyThread(myClass2);

thread1.setName(“钢铁侠”);

thread2.setName(“美国队长”);

//启动线程

thread1.start();

try {

Thread.sleep(1000);//确保 钢铁侠 线程先执行

} catch (InterruptedException e) {

e.printStackTrace();

}

thread2.start();

}

//dosome begin!!!

//doOther begin!!!

//doOther over!!!

//dosome over!!!

//不需要,因为现在有两个对象,对象锁,一个对象一把锁,多少个对象,多少把锁

//现在有两个对象, 钢铁侠 线程占有的是myClass1的对象锁

//与myClass2 的对象锁 没有关系,所以不会影响到 doOther方法的执行

}

class MyThread extends Thread{

MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public synchronized void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

(四)题目四

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

总结

总的来说,面试是有套路的,一面基础,二面架构,三面个人。

最后,小编这里收集整理了一些资料,其中包括面试题(含答案)、书籍、视频等。希望也能帮助想进大厂的朋友

三面蚂蚁金服成功拿到offer后,他说他累了

三面蚂蚁金服成功拿到offer后,他说他累了

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

ls(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public synchronized void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

(四)题目四

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-X0y7yUyH-1712205922394)]
[外链图片转存中…(img-S3a6ySkX-1712205922395)]
[外链图片转存中…(img-BaJpiUs8-1712205922395)]
[外链图片转存中…(img-0SmGneh4-1712205922396)]
[外链图片转存中…(img-aeSWZDNm-1712205922396)]
[外链图片转存中…(img-j1xrYG8L-1712205922396)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-DmlXC44Y-1712205922397)]

总结

总的来说,面试是有套路的,一面基础,二面架构,三面个人。

最后,小编这里收集整理了一些资料,其中包括面试题(含答案)、书籍、视频等。希望也能帮助想进大厂的朋友

[外链图片转存中…(img-gf0VzRug-1712205922397)]

[外链图片转存中…(img-DhYaf8hP-1712205922397)]

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值