并发编程:线程同步工具:4、Phaser 运行阶段性并发任务

并发编程系列之Phaser用法简介 对Phaser阶段协同器的理解,Phaser适用于多个线程协作的任务,分为多个阶段,每个阶段都可以有任意个参与者,线程可以随时注册并参与某个阶段;当一个阶段中所有任务都成功完成后,Phaser的onAdvance()被调用,然后Phaser释放等待线程,自动进入下个阶段。如此循环,直到Phaser不再包含任何参与者。 阅读详情

目录

Phaser

案例说明

一、主程序

二、搜索线程(核心)

三、执行结果


Phaser

当一些并发任务需要分步骤执行时,可以使用该机制。(Phaser类提供了在每一步结束时同步线程的机制,这使得只有当所有线程都完成第一步后,才会有线程开始执行第二步。)

案例说明

下面模拟文件搜索的功能,它拆分为N个阶段进行工作,每个阶段完成后会进入等待,当所有线程都完成该阶段的工作之后,开始执行下一阶段的工作。

 

一、主程序

主程序创建了3个线程,来进行文件的搜索,同时Phaser也设置了3个参与线程数。三个线程统一由phaser对象进行控制,使它们同步得执行每个阶段的工作。

package xyz.jangle.thread.test.n3_5.phaser;

import java.util.concurrent.Phaser;

/**
 * 3.5 Phaser 运行阶段性并发任务
 *   当一些并发任务需要分步骤执行时,可以使用该机制。(Phaser类提供了在每一步结束时同步线程的机制,
 * 这使得只有当所有线程都完成第一步后,才会有线程开始执行第二步。)
 * 
 * @author jangle
 * @email jangle@jangle.xyz
 * @time 2020年8月11日 下午4:58:36
 * 
 */
public class M {

	public static void main(String[] args) {
		Phaser phaser = new Phaser(3);
		FileSearch program86 = new FileSearch("C:\\Program Files (x86)", "log", phaser);
		FileSearch program = new FileSearch("C:\\Program Files", "log", phaser);
		FileSearch workspace = new FileSearch("C:\\workspace", "log", phaser);
		Thread thread1 = new Thread(program86, "program86");
		Thread thread2 = new Thread(program, "program");
		Thread thread3 = new Thread(workspace, "workspace");
		thread1.start();
		thread2.start();
		thread3.start();
		try {
			thread1.join();
			thread2.join();
			thread3.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("程序结束,phaser is terminated:" + phaser.isTerminated());

	}

}

二、搜索线程(核心)

这个线程实现了几个阶段性的工作,并在每个阶段进行等待。                          

  1. 遍历目录搜索所有符合扩展名的文件,并检查是否有记录。                         
  2. 对结果进行过滤,获取近24小时修改过的文件,并检查是否有记录。                    
  3. 输出这些记录。
  4. 完毕注销。
package xyz.jangle.thread.test.n3_5.phaser;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;

/**
 *  这个线程实现了几个阶段性的工作,并在每个阶段进行等待。
 *  1、遍历目录搜索所有符合扩展名的文件,并检查是否有记录。
 *  2、对结果进行过滤,获取近24小时修改过的文件,并检查是否有记录。
 *  3、输出这些记录。
 *  4、完毕注销。
 * @author jangle
 * @email jangle@jangle.xyz
 * @time 2020年8月11日 下午5:02:51
 * 
 */
public class FileSearch implements Runnable {

	// 需要搜索的文件夹路径
	private final String initPath;
	// 要搜索的文件扩展名
	private final String fileExtension;
	// 存储满足条件的文件全路径
	private List<String> results;
	// 对任务的不同阶段进行同步控制。 important
	private Phaser phaser;

	public FileSearch(String initPath, String fileExtension, Phaser phaser) {
		super();
		this.initPath = initPath;
		this.fileExtension = fileExtension;
		this.phaser = phaser;
		this.results = new ArrayList<String>();
	}

	@Override
	public void run() {
		File file = new File(initPath);
		if (file.isDirectory()) {
			// 递归调用,遍历所有目录与其子目录
			directoryProcess(file);
		}
		if (!checkResults()) {
			// 虽然checkResults中从phaser中注销了,但程序还会继续执行,所以这里需要手动return,结束程序。
			return;
		}
		// 筛选近24小时修改过的文件
		filterResults();
		if (!checkResults()) {
			return;
		}
		// 用于最终输出所有结果到控制台
		showInfo();
		// 注销当前任务
		phaser.arriveAndDeregister();
		System.out.println(Thread.currentThread().getName()+"程序执行完毕。");
	}

	/**
	 * 递归调用,遍历所有目录与其子目录
	 * 
	 * @param file
	 */
	private void directoryProcess(File file) {
		File[] files = file.listFiles();
		if (files != null) {
			for (int i = 0; i < files.length; i++) {
				if (files[i].isDirectory()) {
					directoryProcess(files[i]);
				} else {
					fileProcess(files[i]);
				}
			}
		}
	}

	/**
	 * 记录符合扩展名的文件路径
	 * 
	 * @param file
	 */
	private void fileProcess(File file) {
		if (file.getName().endsWith(fileExtension)) {
			results.add(file.getAbsolutePath());
		}
	}

	/**
	 * 筛选近24小时修改过的文件
	 * 
	 * @author jangle
	 * @time 2020年8月11日 下午5:24:05
	 */
	private void filterResults() {
		ArrayList<String> newResult = new ArrayList<>();
		long currentDate = new Date().getTime();
		for (int i = 0; i < results.size(); i++) {
			File file = new File(results.get(i));
			long fileDate = file.lastModified();
			if (currentDate - fileDate < TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS)) {
				newResult.add(results.get(i));
			}
		}
		results = newResult;
	}

	/**
	 * 用于检查每个阶段结束之后,结果是否为空。
	 * 
	 * @author jangle
	 * @time 2020年8月11日 下午5:25:11
	 */
	private boolean checkResults() {
		if (results.isEmpty()) {
			System.out.println(Thread.currentThread().getName() + "结果为空,,阶段phaser:" + phaser.getPhase() + "。该阶段结束,当前存在参与线程数"+phaser.getRegisteredParties());
			// 无结果,则完成当前阶段工作,并且不参与后续阶段的工作。
			phaser.arriveAndDeregister();
			return false;
		} else {
			System.out.println(
					Thread.currentThread().getName() + "存在結果" + results.size() + ",阶段phaser:" + phaser.getPhase()+",当前存在参与线程数"+phaser.getRegisteredParties());
			// 有结果,则完成当前阶段工作,并且等待其他线程完成工作,而后进入后续阶段的工作。
			phaser.arriveAndAwaitAdvance();
			return true;
		}
	}

	/**
	 * 用于最终输出所有结果到控制台
	 * 
	 * @author jangle
	 * @time 2020年8月11日 下午5:41:02
	 */
	private void showInfo() {
		for (int i = 0; i < results.size(); i++) {
			File file = new File(results.get(i));
			System.out.println(Thread.currentThread().getName() + ":" + file.getAbsolutePath());
		}
		// 等待其他线程完成输出,再进入后续工作。
		phaser.arriveAndAwaitAdvance();
	}

}

三、执行结果

前3行是搜索文件数量的结果,3-6行是log文件的数量结果,“参与线程数”是指phaser内部的计数,因program无log文件,提前结束了工作内容,从phaser中注销了,故第6行值为2。

workspace存在結果9,阶段phaser:0,当前存在参与线程数3
program86存在結果13,阶段phaser:0,当前存在参与线程数3
program存在結果10,阶段phaser:0,当前存在参与线程数3
workspace存在結果3,阶段phaser:1,当前存在参与线程数3
program结果为空,,阶段phaser:1。该阶段结束,当前存在参与线程数3
program86存在結果1,阶段phaser:1,当前存在参与线程数2
program86:C:\Program Files (x86)\DingDing\main\current_new\debug.log
workspace:C:\workspace\.metadata\.log
workspace:C:\workspace\.metadata\.plugins\org.eclipse.m2e.logback.configuration\0.log
workspace:C:\workspace\.metadata\.plugins\org.eclipse.rse.core\.log
workspace程序执行完毕。
program86程序执行完毕。
程序结束,phaser is terminated:true

 

CMOS传输门实战:如何用模拟开关优化你的电路设计(附常见问题排查) 本文深入探讨了CMOS传输门在电路设计中的实战应用与优化策略。文章从核心参数选型入手,详细分析了导通电阻、电荷注入等关键特性对电路性能的影响,并提供了多路复用器构建、精密电路应用等实战架构。同时,重点分享了PCB布局、电源去耦的工程细节,以及开关不导通、信号失真、高频性能不足等常见问题的系统性排查指南,旨在帮助硬件工程师充分发挥CMOS模拟开关的效能,提升电路设计的可靠性与精度。 阅读详情

相关推荐

linux redis7 完整的集群搭建过程

一。安装: 1.1 说明: Redis官方只提供了源码,并没有提供经过编译之后的安装包。 因此,安装Redis,要先编译、后安装。(即源码安装方式) 注意:需要联网安装 1.2 安装编译的工具: yum install gcc gcc-c++ -y 1.3 下载tar包,上传到linux服务器,解压配置环境变量 1.4 cd +解压的目录下:编译 make 注意:如果编译失败,请重新进行解压编译,...

m0_38028438的博客 719

Java:102-并发编程详解(中篇)

特性2:层次Phaser 多个Phaser可以组成如下图所示的树状结构,可以通过在构造方法中传入父Phaser来实现 可以发现,在Phaser的内部结构中,每个Phaser记录了自己的父节点,但并没有记录自己的子节点列表,所 以,每个 Phaser 知道自己的父节点是谁,但父节点并不知道自己有多少个子节点,对父节点的操作,是通过子节 点来实现的 树状的Phaser怎么使用呢?考虑如下代码,会组成下图的树状Phaser: 这个64位(long的)的state变量被拆成4部分,下图为state变量各部分

qq_59609098的博客 1098

SDIO_WiFi驱动学习之SDIO架构介绍及源码分析

一、引言 因为WiFi驱动比较复杂,所以WiFi驱动的博客将分为两篇来写。 本篇博客主要介绍Linux下的SDIO架构及源码分析。 二、SDIO WiFi SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈,能够实现用户主平台数据通过SDIO口到无线网络之间的转换。SDIO具有传输数据快,兼容SD、MMC接口等特点。 对于SDIO接口的WiFi,首先,它是一个SDIO的卡设备,然后具备了WiFi的功能。所

陈子陌的博客 1万+

Java并发编程-Phaser解析

在并发编程中,协调多个线程之间的执行顺序是一个常见的需求。Java 提供了多种工具来处理线程同步问题,如和Semaphore。然而,这些工具在某些复杂的场景中显得有些笨拙或受限。为了提供一种更灵活、更强大的阶段性同步机制,Java 并发包引入了Phaser类。Phaser是 Java 7 引入的一个高级同步工具,它允许一组线程在多个阶段上进行同步,而不是像那样只在一个点上进行同步。这使得Phaser在处理多阶段并发任务时特别有用,尤其是在动态调整参与线程数量的情况下。通过Phaser。

WenZhang的博客 1181

基于多时间尺度的电动汽车光伏充电站联合分层优化调度(Matlab代码实现)

该研究提出了一种电动汽车光伏充电站的分层优化调度方法,采用日前-日内两阶段协同优化框架。日前阶段通过混合整数规划对多充电站进行全局优化,以平抑电网波动和降低购电成本为目标;日内阶段基于实时数据,采用NSGA-II等算法动态分配充电功率。该方法实现了光伏高效消纳、成本节约和电网负荷平衡的有效统一。

zhangxd212489的博客 518

别再手动调参了!用BERTopic分析500篇论文摘要,我总结了这份超全参数调优指南

本文详细介绍了如何通过BERTopic参数调优从500篇论文摘要中挖掘高质量主题。从文档嵌入、降维到聚类,每个环节的关键参数设置都直接影响主题分析效果。文章提供了学术文本特有的调优技巧,包括预处理、主题多样性与专注度的平衡,以及评估指标与迭代优化方法,帮助研究者高效完成NLP主题分析任务。

weixin_31235909的博客 214

Java轻量级锁原理与应用

在当今的软件开发领域,高并发已经成为一个不可忽视的关键问题。尤其是在Java这种广泛应用于企业级应用的语言中,如何有效地处理并发访问,成为了开发者必须面对的挑战。在这个背景下,轻量级锁作为一种高效的处理并发机制,其重要性不言而喻。想象一下,在一个多线程环境中,如果每个线程在访问共享资源时都需要进行重量级的同步操作,那么系统的性能将会受到极大的影响。而轻量级锁通过减少锁的粒度和降低锁的获取成本,能够显著提高并发性能。接下来,我们将从概念、背景和优势三个方面,深入探讨Java高并发知识点之轻量级锁的奥秘。

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

YOLOv8+BiFPN双向特征金字塔:多尺度目标检测精度提升28%的实战教程

双向特征金字塔网络(BiFPN)是一种强化多尺度特征融合支持“自顶向下+自底向上”的双向特征流动,同时保留不同尺度的细节信息;引入加权融合机制,自动学习不同特征的重要性权重;裁剪冗余节点,在轻量化的同时提升融合效率。

m0_64661770的博客 492

java并发编程之Phaser的使用

通过使用CyclicBarrier类解决了CountDownLatch类的种种缺点,但不可否认的是,CyclicBarrier类还是有一些自身上的缺陷,比如不可以动态添加parties计数,调用一个await()方法仅仅占用一个parties计数,所以jdk7中新增一个名称为Phaser的类来解决这样的问题。 3.2类Phaser的arriveAndAwaitAdvance()的方法 //创建一个...

大鹏视界U 1081

Hermes Agent 常见报错排查与问题解决

Hermes Agent 常见报错排查指南 摘要 本文全面梳理了 Hermes Agent 使用过程中的10类常见报错,按阶段划分为安装期、运行期认证类、后端基础设施类和长任务执行类四大类,并提供了系统化的排查流程: 安装期问题:包括命令未找到和Python版本不兼容,主要检查PATH环境和Python版本是否符合要求 认证类问题:涉及API Key认证失败和速率限制,需要验证密钥配置和配额状态 基础设施问题:包含Docker报错、MCP连接失败和OAuth令牌过期,需检查相关服务状态 执行类问题:主要是上

简简单单Onlinezuozuo 1343

ANPC三电平拓扑双脉冲试验的原理与实施

ANPC三电平拓扑因损耗低、谐波小、应力均衡而广泛应用,但其存在短、长两类换流通道,寄生电感和开关应力差异显著。短通道尖峰小、可靠性高;长通道应力严苛。因此需分别制定两套独立双脉冲测试方案,针对不同时段和器件(如T1、T5、T6、T4及对应二极管)全面考核开关特性。

嵌入式老牛的博客 346

Java 入门指南:Java 并发编程 —— 同步工具类 Phaser(相位器)

Phaser(相位器)是Java中的一种同步辅助类,用于控制多个线程的阶段性任务同步。它提供了更灵活和高级的同步机制,可以动态地适应参与线程的数量。 Phaser 可以看作功能增强的 CyclicBarrier,与 CyclicBarrier(循环屏障) 和 CountDownLatch(倒计时门闩) 等传统同步工具相比,Phaser 提供了更灵活和更高级的功能,特别是在处理动态和可变的并行任务集合时。

热带鱼的技术博客 1667

基于STM32、HAL库的ADE7880ACPZ-RL电能计量芯片驱动程序设计

@retval 状态 (ADE7880_OK或ADE7880_ERROR)* @retval 状态 (ADE7880_OK或ADE7880_ERROR)* @retval 状态 (ADE7880_OK或ADE7880_ERROR)* @retval 状态 (ADE7880_OK或ADE7880_ERROR)* @retval 状态 (ADE7880_OK或ADE7880_ERROR)PA6 (MISO) <--> SDO (SPI数据输出)PB0 (CS) <--> /CS (片选,低有效)

corlin6688的博客 667

并发工具Phaser、Exchanger使用

前言 Phaser是JDK1.7提供的一种同步工具,是可重用的同步屏障,其功能类似于CyclicBarrier和CountDownLatch,但支持更灵活的用法。本篇主要讲解Phaser、Exchanger两种工具的用法。 其更加灵活的地方就在于:可以阶段性的控制各个线程的行为。 举个例子 总共六位同学要参加同学聚会,聚会流程有三项:吃饭、K歌、看电影。六位同学都要吃饭,但是吃完饭后只有四位同学想...

Sicimike的博客 3272

假人有限元模型

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

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

并发编程:线程同步工具:4、Phaser(onAdvance方法) 阶段性并发任务中阶段转变的控制

Phaser的onAdvance方法,实现阶段性并发任务中阶段转变的控制。 即在阶段之间执行一点逻辑,并可以干预其是否继续执行后续阶段的内容。

博风_刺耳的博客(网络记事本) 704

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

并发类Semaphore、Phaser、CountDownLatch的学习

本文介绍了Java中三种常用的线程同步工具:CountDownLatch、Semaphore和Phaser。CountDownLatch用于等待多个任务完成,适合一次性同步场景;Semaphore通过控制许可数量实现资源访问限制,适用于限流和资源池管理;Phaser则支持多阶段同步和动态参与者调整,适用于复杂的多轮协作场景。文章还分析了死锁的四个必要条件及解决策略,并对比了三种同步工具的特性差异。这些工具都基于AQS实现,可以替代传统的wait/notify机制,提供更安全高效的线程协调方案。

weixin_50055999的博客 1496

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Java并发编程:阶段同步器原理与应用

在当今的互联网时代,Java作为一门广泛应用于企业级应用开发的语言,其并发处理能力成为了衡量系统性能的关键指标。在多线程环境下,如何有效地同步线程间的操作,确保数据的一致性和系统的稳定性,成为了开发人员必须面对的挑战。本文将围绕Java高并发知识点之阶段同步器展开讨论,旨在深入理解其概念与作用。在现实应用中,我们常常会遇到这样的场景:多个线程需要按照特定的顺序执行,或者需要等待某个条件成立后才能继续执行。

MoneyHacksPro的博客 816

JDK并发:同步工具类(Stemaphore,CountDownLatch,CyclicBarrier,Exchanger,Phaser)

在现代Java开发中,处理并发问题至关重要,而同步工具类是实现高效多线程程序的关键。本博客将深入探讨Java中的多种同步工具,包括信号量、等待完成、阶段同步、Exchanger和Phaser。我们将逐一解析这些工具的使用场景及其背后的原理,帮助你更好地理解它们在并发控制中的角色。无论你是Java新手还是经验丰富的开发者,本文都将为你提供实用的知识和技巧,助你在并发编程的世界中游刃有余。

燕双嘤 954

线程辅助类 —— CyclicBarrier、Phaser、Exchanger 解析

Semaphore、CountDownLatch、CyclicBarrier、Phaser、Exchanger 介绍。

一步一步往上爬 1197

Java 锁相关详解【七、Java 并发工具类全景图:Semaphore、CountDownLatch、CyclicBarrier 与 Phaser】

本文全面解析了Java四大并发工具类:Semaphore用于控制资源访问数量(如限流);CountDownLatch用于等待一组线程完成(一次性);CyclicBarrier实现线程同步执行(可重用);Phaser支持分阶段动态同步(复杂任务)。四者各具特色:Semaphore基于许可控制并发,CountDownLatch采用倒计时机制,CyclicBarrier通过屏障同步,Phaser提供多阶段灵活性。文章通过代码示例展示了它们的典型应用场景,并对比了核心特性和适用场景,建议开发者根据具体需求选择合适工

NeoLshu的博客 754

【Java线程安全实战】⑧ 阶段同步的艺术:Phaser 与 Condition 的高阶玩法

掌握多阶段任务的线程同步是Java并发编程的核心挑战。本文深入解析Phaser与Condition的高阶用法:Phaser提供动态阶段同步能力,可灵活管理线程参与的阶段;Condition则实现细粒度的等待/唤醒机制。通过快递中心多阶段运营的生动案例,详解两者结合的最佳实践,包括Phaser动态注册、Condition虚假唤醒预防、协调线程设计等关键技巧。文章提供可运行的代码示例,助你摆脱传统同步工具的局限,构建更高效、灵活的并发系统。

xiezhiyi007的专栏 656

6、JUC并发同步工具类应用与实战

ReentrantLock的应用场景主要体现在多线程环境下对共享资源的独占式访问,以保证数据的一致性和安全性。ReentrantLock具体应用场景如下:1.解决多线程竞争资源的问题,例如多个线程同时对同一个数据库进行写操作,可以使用ReentrantLock保证每次只有一个线程能够写入。2.实现多线程任务的顺序执行,例如在一个线程执行完某个任务后,再让另一个线程执行任务。3.实现多线程等待/通知机制,例如在某个线程执行完某个任务后,通知其他线程继续执行任务。

sproutBoy的博客 2085

JUC并发工具类全解析:CountDownLatch、CyclicBarrier、Semaphore 与 Phaser 实战指南

需求推荐工具等待 N 个独立任务完成多线程循环同步(固定参与者)控制并发访问数量Semaphore动态参与者 + 多阶段同步PhaserSemaphore和Phaser是 Java 并发编程的“瑞士军刀”。理解其设计思想与适用场景,能让你在面对复杂同步需求时游刃有余。工具本身不难,难的是在正确的时间使用正确的工具。行动建议在你的项目中识别是否存在“手动 wait/notify”可被替换的场景用Semaphore重构现有连接池或限流逻辑尝试用Phaser实现一个多阶段数据处理 pipeline。

吾为热爱计科,致力于知识分享的践行者,此博客是我在这条道路上留下的足迹。 968

JUC并发工具类--Phaser(阶段协同器)

JUC并发工具类--Phaser(阶段协同器)

qq_41139119的博客 857

java并发编程-Phaser的使用

Phaser介绍 在生活中我们会遇到这样的场景:我们把一个任务分成多个阶段来做,每个阶段可以由多人来做,当一个阶段所有人的任务都完成后,才能进入下一个阶段,按这样的方式来完成任务直到任务完成。 类似于一个阶段性事务。那么在java编程中如何实现呢?可以通过并发包中的Phaser来模拟实现该场景。 Java中的Phaser也是一个同步barrier(屏障),就像CountDownLatch和CyclicBarrier一样,线程需要在barrier处等待,直到所有线程都达到barrier,也就是barrier被

zg_hover的专栏 823

Java 并发工具进阶:Phaser 的分阶段任务管理与实战

当任务存在可变阶段数或线程规模需要动态调整时,优先选择 Phaser 替代 CyclicBarrier/CountDownLatch。Phaser 通过其灵活的分阶段管理机制,完美解决了复杂多阶段任务中的线程协同问题,特别适合需要动态调整并发规模的任务流场景。,适用于多线程分批次执行的场景。Phaser 是 Java 并发包中的。

2501_93895648的博客 419
上一篇: 并发编程:线程同步工具:3、CyclicBarrier 在指定状态点同步任务
下一篇: 并发编程:线程同步工具:4、Phaser(onAdvance方法) 阶段性并发任务中阶段转变的控制
博风
博客等级 码龄16年 442粉丝 357原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值