带你深入理解Java线程间通信

Java多线程——线程通信 多线程 线程通信 唤醒 等待线程 阅读详情

合理的使用Java多线程可以更好地利用服务器资源。一般来讲,线程内部有自己私有的线程上下文,互不干扰。但是当我们需要多个线程之间相互协作的时候,就需要我们掌握Java线程的通信方式。本文将介绍Java线程之间的几种通信原理。

锁与同步

在Java中,锁的概念都是基于对象的,所以我们又经常称它为对象锁。一个锁同一时间只能被一个线程持有。也就是说,一个锁如果被一个线程所持有,那其他线程如果需要得到这个锁,就得等这个线程释放该锁。

线程之间,有一个同步的概念。在多线程中,可能有多个线程试图访问一个有限的资源,必须预防这种情况的发生。

所以引入了同步机制:在线程使用一个资源时为其加锁,这样其他的线程便不能访问那个资源了,直到解锁后才可以访问。线程同步是线程之间按照一定的顺序执行。

我们先来看看一个无锁的程序:

public class NoLock { static class ThreadA implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("Thread A " + i); } } } static class ThreadB implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("Thread B " + i); } } } public static void main(String[] args) { new Thread(new ThreadA()).start(); new Thread(new ThreadB()).start(); } } 复制代码

执行这个程序,你会在控制台看到,线程A和线程B各自独立工作,输出自己的打印值。每一次运行结果都会不一样。如下是我的电脑上某一次运行的结果。

... Thread B 74 Thread B 75 Thread B 76 Thread B 77 Thread B 78 Thread B 79 Thread B 80 Thread A 3 Thread A 4 Thread A 5 Thread A 6 Thread A 7 Thread A 8 Thread A 9 ... 复制代码

现在有一个需求,想等A先执行完之后,再由B去执行,怎么办呢?最简单的方式就是使用一个“对象锁”。

public class ObjLock { private static Object lock = new Object(); static class ThreadA implements Runnable { @Override public void run() { synchronized (lock) { for (int i = 0; i < 100; i++) { System.out.println("Thread A " + i); } } } } static class ThreadB implements Runnable { @Override public void run() { synchronized (lock) { for (int i = 0; i < 100; i++) { System.out.println("Thread B " + i); } } } } public static void main(String[] args) throws InterruptedException { new Thread(new ThreadA()).start(); Thread.sleep(10); new Thread(new ThreadB()).start(); } } 复制代码

这里声明了一个名字为lock的对象锁。我们在ThreadA和ThreadB内需要同步的代码块里,都是用synchronized关键字加上了同一个对象锁lock。

上文我们说到了,根据线程和锁的关系,同一时间只有一个线程持有一个锁,那么线程B就会等线程A执行完成后释放lock,线程B才能获得锁lock。

这里在主线程里使用sleep方法睡眠了10毫秒,是为了防止线程B先得到锁。因为如果同时start,线程A和线程B都是出于就绪状态,操作系统可能会先让B运行。这样就会先输出B的内容,然后B执行完成之后自动释放锁,线程A再执行。

等待/通知机制

上面一种基于“锁”的方式,线程需要不断地去尝试获得锁,如果失败了,再继续尝试。这可能会耗费服务器资源。 而等待/通知机制是另一种方式。

Java多线程的等待/通知机制是基于Object类的wait()方法和notify(), notifyAll()方法来实现的。

notify()方法会随机叫醒一个正在等待的线程,而notifyAll()会叫醒所有正在等待的线程。

前面我们讲到,一个锁同一时刻只能被一个线程持有。而假如线程A现在持有了一个锁lock并开始执行,它可以使用lock.wait()让自己进入等待状态。这个时候,lock这个锁是被释放了的。

这时,线程B获得了lock这个锁并开始执行,它可以在某一时刻,使用lock.notify(),通知之前持有lock锁并进入等待状态的线程A,说“线程A你不用等了,可以往下执行了”。

需要注意的是,这个时候线程B并没有释放锁lock,除非线程B这个时候使用lock.wait()释放锁,或者线程B执行结束自行释放锁,线程A才能得到lock锁。

用代码来实现一下:

public class WaitNotify { private static Object lock = new Object(); static class ThreadA implements Runnable { @Override public void run() { synchronized (lock) { for (int i = 0; i < 5; i++) { try { System.out.println("ThreadA: " + i); lock.notify(); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } lock.notify(); } } } static class ThreadB implements Runnable { @Override public void run() { synchronized (lock) { for (int i = 0; i < 5; i++) { try { System.out.println("ThreadB: " + i); lock.notify(); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } lock.notify(); } } } public static void main(String[] args) throws InterruptedException { new Thread(new ThreadA()).start(); Thread.sleep(1000); new Thread(new ThreadB()).start(); } } // 输出: ThreadA: 0 ThreadB: 0 ThreadA: 1 ThreadB: 1 ThreadA: 2 ThreadB: 2 ThreadA: 3 ThreadB: 3 ThreadA: 4 ThreadB: 4 复制代码

线程A和线程B首先打印出自己需要的东西,然后使用notify()方法叫醒另一个正在等待的线程,然后自己使用wait()方法陷入等待并释放lock锁。

需要注意的是等待/通知机制使用的是使用同一个对象锁,如果你两个线程使用的是不同的对象锁,那它们之间是不能用等待/通知机制通信的。

信号量--Volatile

信号量(Semaphore) :有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。

本文不是要介绍这个类,而是介绍一种基于volatile关键字的自己实现的信号量通信。

volatile关键字能够保证内存的可见性,如果用volatile关键字声明了一个变量,在一个线程里面改变了这个变量的值,那其它线程是立马可见更改后的值的。

比如我现在有一个需求,我想让线程A输出0,然后线程B输出1,再然后线程A输出2…以此类推。我应该怎样实现呢?

public class Count { private static volatile int count = 0; static class ThreadA implements Runnable { @Override public void run() { while (count < 5) { if (count % 2 == 0) { System.out.println("threadA: " + count); synchronized (this) { count++; } } } } } static class ThreadB implements Runnable { @Override public void run() { while (count < 5) { if (count % 2 == 1) { System.out.println("threadB: " + signal); synchronized (this) { count++; } } } } } public static void main(String[] args) throws InterruptedException { new Thread(new ThreadA()).start(); Thread.sleep(1000); new Thread(new ThreadB()).start(); } } // 输出: threadA: 0 threadB: 1 threadA: 2 threadB: 3 threadA: 4 复制代码

我们可以看到,使用了一个volatile变量count来实现了“信号量”的模型。这里需要注意的是,volatile变量需要进行原子操作,而count++并不是一个原子操作,根据需要使用synchronized给它“上锁”,或者是使用AtomicInteger等原子类。

信号量的应用场景

假如在一个停车场中,车位是我们的公共资源,线程就如同车辆,而看门的管理员就是起的“信号量”的作用。 因为在这种场景下,多个线程需要相互合作,我们用简单的“锁”和“等待通知机制”就不那么方便了。这个时候就可以用到信号量。

管道输入/输出流

管道是基于“管道流”的通信方式。JDK提供了PipedWriter、 PipedReader、 PipedOutputStream、 PipedInputStream。

其中,前面两个是基于字符的,后面两个是基于字节流的。

以下示例代码使用的是基于字符的:

public class Pipe { static class ReaderThread implements Runnable { private PipedReader reader; public ReaderThread(PipedReader reader) { this.reader = reader; } @Override public void run() { System.out.println("this is reader"); int receive = 0; try { while ((receive = reader.read()) != -1) { System.out.print((char)receive); } } catch (IOException e) { e.printStackTrace(); } } } static class WriterThread implements Runnable { private PipedWriter writer; public WriterThread(PipedWriter writer) { this.writer = writer; } @Override public void run() { System.out.println("this is writer"); int receive = 0; try { writer.write("test"); } catch (IOException e) { e.printStackTrace(); } finally { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws IOException, InterruptedException { PipedWriter writer = new PipedWriter(); PipedReader reader = new PipedReader(); writer.connect(reader); new Thread(new ReaderThread(reader)).start(); Thread.sleep(1000); new Thread(new WriterThread(writer)).start(); } } // 输出: this is reader this is writer test 复制代码

我们通过线程的构造函数,传入了PipedWrite和PipedReader对象。可以简单分析一下这个示例代码的执行流程:

  1. 线程ReaderThread开始执行,

  2. 线程ReaderThread使用管道reader.read()进入”阻塞“,

  3. 线程WriterThread开始执行,

  4. 线程WriterThread用writer.write("test")往管道写入字符串,

  5. 线程WriterThread使用writer.close()结束管道写入,并执行完毕,

  6. 线程ReaderThread接受到管道输出的字符串并打印,

  7. 线程ReaderThread执行完毕。

管道通信的应用场景

这个很好理解。使用管道多半与I/O流相关。当我们一个线程需要先另一个线程发送一个信息(比如字符串)或者文件等等时,就需要使用管道通信了。

Thread.join()方法

join()方法是Thread类的一个实例方法。它的作用是让当前线程陷入“等待”状态,等join的这个线程执行完成后,再继续执行当前线程。

有时候,主线程创建并启动了子线程,如果子线程中需要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。

如果主线程想等待子线程执行完毕后,获得子线程中的处理完的某个数据,就要用到join方法了。

示例代码:

public class Join { static class ThreadA implements Runnable { @Override public void run() { try { System.out.println("我是子线程,我先睡一秒"); Thread.sleep(1000); System.out.println("我是子线程,我睡完了一秒"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new ThreadA()); thread.start(); thread.join(); System.out.println("如果不加join方法,我会先被打出来,加了就不一样了"); } } 复制代码

ThreadLocal类

ThreadLocal是一个本地线程副本变量工具类。内部是一个弱引用的Map来维护。

严格来说,ThreadLocal类并不属于多线程间的通信,而是让每个线程有自己”独立“的变量,线程之间互不影响。它为每个线程都创建一个副本,每个线程可以访问自己内部的副本变量。

ThreadLocal类最常用的就是set方法和get方法。示例代码:

public class Profiler { private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>() { protected Long initialValue() { return System.currentTimeMillis(); } }; public static void begin() { TIME_THREADLOCAL.set(System.currentTimeMillis()); } public static long end() { return System.currentTimeMillis() - TIME_THREADLOCAL.get(); } public static void main(String[] args) throws InterruptedException { Profiler.begin(); TimeUnit.SECONDS.sleep(1); System.out.println("耗时:" + Profiler.end() + "mills"); } } // 输出: 耗时:1001mills 复制代码

Profiler 可以被复用在方法的耗时统计的功能上,在方法的入口前执行begin()方法,在方法调用后执行end()方法,好处是两个方法的调用不用在一个方法和类中,比如在AOP(面向切面编程)中,可以在方法调用前的切入点执行begin()方法,而在方法调用切入点执行end()方法,这样依旧可以获得方法的执行耗时。

ThreadLocal的应用场景

最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等。数据库连接和Session管理涉及多个复杂对象的初始化和关闭。如果在每个线程中声明一些私有变量来进行操作,那这个线程就变得不那么“轻量”了,需要频繁的创建和关闭连接。

小结

线程间通信使线程成为一个整体,提高系统之间的交互性,在提高CPU利用率的同时可以对线程任务进行有效的把控与监督。

Java多线程--线程间的通信 阅读详情

相关推荐

27、JAVA进阶——线程同步和线程间的通信

一、线程同步的必要性 二、实现线程同步 1. 同步方法 2. 同步代码块 3. 死锁 三、线程间通信的必要性 四、实现线程间通信

不迁怒,不贰过。小知识,大智慧。 1320

Java线程之间如何通信的,有哪些方式?

尽可能避免共享变量,因为共享变量容易引起线程安全问题。对共享变量的访问需要加锁,以保证同步访问。避免死锁问题的发生。使用锁时需要特别注意加锁和释放锁的顺序。使用条件变量时,应该确保线程在调用 await() 方法之前已经获取了锁。否则,在等待条件成立的过程中,其他线程可能会修改共享资源,导致程序出现错误。 在使用信号量时,需要正确设置信号量的个数和调用 acquire() 和 release() 方法的顺序,否则可能会出现线程阻塞或信号量无法释放的情况。管道的使用需要注意线程之间的数据传输方式,以及缓冲区的

Ascend1977的博客 6796

基于Matlab的MIMO通信系统仿真_Simulink实现

瑞利衰落信道中使用空时分组码的BPSK调制的BER 1、2发1收 参数设置: 双极性信号元+1和-1,每符号含1bit; 采样时间=符号时间=1s; 采用基于数据帧的仿真计算模式,每帧中有2个采样值; 仿真时间=出现100个误码后停止仿真; 随机整数种子=22; Jakes衰落模型中每个信道上的多普勒频移=0.01Hz; 输人信号功率=2W; Alamouti码:两根发送天线,一根接收天线(分集阶= 2); 平均信道信噪比= 18dB; 仿真得出的误码率统计BER =0. 00017. 2、2发

太阳帅帅的博客 9688

Java线程间通信方式

前文了解了线程的创建方式和状态切换,在实际开发时,一个进程中往往有很多个线程,大多数线程之间往往不是绝对独立的,比如说我们需要将A和B 两个线程的执行结果收集在一起然后显示在界面上,又或者比较典型的消费者-生产者模式,在这些场景下,线程间通信成了我们必须使用的手段,那么线程之间怎么通信呢?线程间通信方式,从实现本质来讲,主要可以分为两大类共享内存和消息传递。

小海编码日记 1421

转义序列字符(\0,\n,\r,\t,\v,\a,\f,\b,\\,\‘,\“,\?)详解,转义字符对应的全称,输出结果和对应的ASCII码值详解;

转义序列字符(\0,\n,\r,\t,\v,\a,\f,\b,\\,\',\",\?)详解,转义字符对应的全称,输出结果和对应的ASCII码值详解;

南城花开过的博客 3万+

SAP公司间外协业务流程以及配置说明

SAP公司间外协业务流程以及配置说明

竹大的博客 7157

Java并发学习 7: 线程之间的通信

3.1 继承 Thread类 // 继承,则Demo1 也成为了一个线程 public class Demo1 extends Thread { // 继承Thread类,需要重写run()方法,告诉这个类需要去坐什么. @Override public void run() { // 这个新类,只需要打印出自己的名字(每个线程都有自己的Name属性并提供g...

过道 3225

Labview及NI-USRP驱动安装教程

软件下载 安装使用的LabVIEW2018及NI-USRP驱动程序已上传,下载地址: 天翼云盘链接 (访问码:oc33) 百度网盘链接 (提取码:88mi) (注:天翼云盘下载不限速) LabVIEW2018安装步骤 右键软件压缩包选择解压至LabVIEW2018 选中文件,鼠标右击选择以管理员身份打开2018LV-WinChn.exe文件。 点击确定。 点击Unzip解压。 点击确定。 点击下一步。 填写任意信息后,点击下一步。 点击下一步。 点击浏览更改安装路径建议安装到除C盘以

dadapongi6的博客 2万+

深入理解线程间通信方式

线程通信是并发编程的核心需求,其本质在于协调多线程间的执行顺序、共享数据访问和状态通知。本文通过六大反面案例揭示了线程通信的必要性:1)共享变量未同步导致库存超卖;2)不使用wait/notify造成CPU空转;3)普通队列引发日志丢失;4)未用CountDownLatch导致系统启动失败;5)并行计算缺乏协调产生错误结果;6)静态变量引发用户信息污染。针对这些问题,文章提出了同步锁、阻塞队列、ThreadLocal等解决方案,并强调应优先使用高层抽象工具(如BlockingQueue)和线程封闭技术,避免

jdjdhghl的博客 1603

NA400与昆仑通态MODBUS/TCP通讯调试说明

点击增加设备通道,弹出窗口进行设置,通道类型选择“0区输出继电器”,通道地址为1,通道个数为1,读写方式为读写。点击增加设备通道,弹出窗口进行设置,通道类型选择“4区输出寄存器”,数据类型选择“16位 无符号二进制”,通道地址为21001,通道个数为1,读写方式为读写。5.添加设备通道:双击莫迪康MODBUSTCP子设备,点击添加设备通道,根据Modbus规约地址表进行参数的设置,表格可在NAPro软件帮助中查看,如下图所示。然后可以在设备通道调试数据中观察到Q1的值为1,NW1的值678,表示通讯成功。

weixin_44281509的博客 2490

在VINS-FUSION上处理KITTI数据集(双目+GPS)

VINS-Fusion 是一个开源的实时多传感器状态估计库,主要由香港科技大学的沈邵劼教授领导的研究团队开发。它是 VINS-Mono(单目视觉惯性系统)的扩展,支持多种传感器组合,如双目、立体相机和IMU。

如果想成为中心,那么就到中心去吧。 1203

C++ 线程间通信开发从入门到精通实战

*线程间通信(Inter-Thread Communication,简称ITC)**是指在多线程程序中,一个线程与另一个线程之间交换信息或数据的过程。由于多个线程共享同一进程的地址空间,线程间通信需要协调访问共享资源,确保数据的一致性和完整性。线程间通信是多线程编程中的关键技术,决定了多线程程序的正确性和性能。在C++中,通过互斥锁、条件变量、原子操作、读写锁等多种同步原语,开发者可以实现高效、安全的线程间通信机制。然而,随着程序复杂度的增加,合理使用和优化这些同步工具显得尤为重要。关键要点。

编程技术探索者,分享C/C++、C#、Java、数据库等开发经验,聚焦实战技巧与AI兴趣,助力编程爱好者成长。 1397

基于GA-PSO-BP混合神经网络的多输入多输出数据预测-Python代码实现

多输入多输出数据预测

2301_80449389的博客 2783

深入理解Java线程间通信

合理的使用Java多线程可以更好地利用服务器资源。一般来讲,线程内部有自己私有的线程上下文,互不干扰。但是当我们需要多个线程之间相互协作的时候,就需要我们掌握Java线程的通信方式。本文将介绍Java线程之间的几种通信原理。

BASK2311的博客 1107

Unity Shader之路(一)什么是Unity Shader?

Unity Shader前言什么是UnityShaderUnity Shader和材质的关系 前言 众所周知,要想当游戏开发工程师,Unity中的Shader编写不得不摸,这一系列文章我将描写我的Shader学习之路,致敬冯乐乐的书Unity Shader入门精要,此篇什么是Unity Shader?希望能给后人一点方向 我们刚开始接触Unity的时候,总是会遇到材质,但是不了解材质都是什么,为什么可以在很多材质中选择,为什么有时候选择了其他材质后就不能达到我们想要的结果,为什么资源商店有那么多的材质,山.

TurboQ珞珈 2237

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Java线程间的通信方式

参考:Java并发编程的艺术 线程间通信的方式有: 通过volatile和synchronized关键字 等待/通知机制 管道输入/输出流 Thread.join ThreadLocal线程本地变量 1 通过volatile和synchronized关键字 Java支持多个线程同时访问一个对象或者对象的成员变量。 每个线程在自己的本地内存中都有共享变量的一份拷贝。 volatile volatile关键字用来修饰变量。任何对该变量的访问均需要从共享内存中获取,任何对该变量的修改必须同步刷新到共.

西瓜游侠的博客 675

数学建模模型总结

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

java线程间通信方式_深入理解JAVA多线程之线程间的通信方式

一,介绍本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码。二,线程间的通信方式①同步这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。参考示例:public class MyObject {synchronized public void methodA() {//do somethin...

weixin_30918493的博客 117

计算机控制技术及应用

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

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

c++线程间通信的几种方法_学Java必备的线程基础

熟悉Java开发的同学都知道,Java天生支持多线程编程的。这篇文章我们主要来学习下Java线程的基础知识,从线程的启动到不同线程间的通信方式,目的是更系统的掌握Java线程基础。 本文的讲解主要从以下几个点展开:什么是线程线程都有哪几种状态线程的启动和终止线程间的通信利用本文讲解的线程知识,实现一个简单的线程池如果上面列出的这几个点,你都已经熟练的掌握了,那么可能本文就无法给你带来帮助了。选择性...

weixin_36019798的博客 1590

【Java面试题】进程间通信的方式有哪些?线程间通讯方式有哪些?

Java面试题:进程间通信的方式有哪些?线程间通讯方式有哪些? 一、进程间的通信方式 管道( pipe ): 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 信号量(semophore ) : 信号量是一个计数器,可以用来...

郑清 6413

详细介绍线程间通信

线程间通信是指在多线程编程中,不同的线程之间通过某种方式交换信息的过程。这是一个重要的概念,因为线程之间的协作是实现复杂并发系统的关键。

weixin_43740011的博客 1593

深入理解(15)java多线程之Lock对象实现同步以及线程间通信

Lock对象简介 这里为什么说Lock对象哪?Lock其实是一个接口,在JDK1.5以后开始提供,其实现类常用的有ReentrantLock,这里所说的Lock对象即是只Lock接口的实现类,为了方便记忆或理解,都简称为Lock对象。 我们知道synchronized关键字可以实现线程间的同步互斥,从JDK1.5开始新增的ReentrantLock类能够达到同样的效果,并且在此基础上还扩展了很多实...

cowbin2012的专栏 1万+

Java高并发:线程间通信机制详解

在当今的软件开发领域,高并发已经成为一个不可忽视的关键问题。尤其是在处理大量用户请求或进行复杂计算时,如何有效地实现线程间的通信成为了一个至关重要的技术挑战。以一个典型的在线购物平台为例,当用户发起购物车更新请求时,系统需要确保订单处理线程与购物车线程之间的数据同步,以避免数据不一致的问题。这就需要我们深入理解Java高并发知识点中的线程间通信。线程间通信是Java并发编程中的一个核心概念,它涉及到多个线程之间的数据共享和同步。在实际开发中,线程间通信对于确保程序的正确性和稳定性具有重要意义。

我是Java程序员廖志伟,感谢朋友们的支持!不定期贡献一篇高品质、过万文字、图文并茂且附有视频解说、满载代码示例注释的良心之作,坚决杜绝粗制乱造。 860

你知道 Java 是如何实现线程间通信的吗?

正常情况下,每个子线程完成各自的任务就可以结束了。不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了。 本文涉及到的知识点: thread.join(), object.wait(), object.notify(), CountdownLatch, CyclicBarrier, FutureTask, Callable 。 本文涉及代码: https://github.com/wingjay/Hello

HY1273383167的博客 173
上一篇: 你认为低代码能够完全取代程序猿吗?
下一篇: 想过为什么家里的IP是192.168开头的吗?
Java_LingFeng
博客等级 码龄4年 505粉丝 259原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值