JUC系列(七) ForkJion任务拆分与异步回调

Fork/Join 框架详解 Fork/Join 是一种并行计算模式,用于解决可以被分解成更小的可并行任务的问题。该模式包含两个关键操作:Fork(分解)和Join(合并)。在 Fork/Join 模式中,原始问题被递归地分解为更小的子问题,直到达到可以并行解决的最小单位。这个过程被称为 Fork。每个子问题可以独立地在不同的处理器上执行,并行地求解部分问题。一旦所有的子问题都被解决,就会进行 Join 操作。Join 操作将所有子问题的结果合并为最终的解决方案。 阅读详情

📣 📣 📣 📢📢📢
☀️☀️你好啊!小伙伴,我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。
📒 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️,擅长使用Java技术开发web项目和工具
📒 文章内容丰富:覆盖大部分java必学技术栈,前端,计算机基础,容器等方面的文章
📒 如果你也对Java感兴趣,关注小冷吧,一起探索Java技术的生态与进步,一起讨论Java技术的使用与学习
✏️高质量技术专栏专栏链接: 微服务,数据结构,netty,单点登录,SSM ,SpringCloudAlibaba等
😝公众号😝 : 想全栈的小冷,分享一些技术上的文章,以及解决问题的经验
⏩当前专栏:JUC系列

ForkJion

什么是ForkJoin

ForkJoin 下 JDK 1.7 并行执行任务的,数量越大,效率越高

比如 :大数据 Map Reduce(把大任务拆分成小任务)

image-20220304004113183

ForkJoin 特点: 工作窃取

举例子:

PS: 维护的是双端队列 Deuue

A线程执行任务到 第二个

B线程执行完毕,那么B线程回去讲A线程的东西拿来执行,从而提高效率

image-20220304004235249

认识forkjion

ForkJoin 使用两个类来完成以上两件事情:

  • ForkJoinTask:我们要使用 ForkJoin 框架,必须首先创建一个 ForkJoin 任务。它提供在任务中执行 fork() 和 join() 操作的机制,通常情况下我们不需要直接继承 ForkJoinTask 类,而只需要继承它的子类,Fork/Join 框架提供了以下两个子类:
    • RecursiveAction:用于没有返回结果的任务。
    • RecursiveTask :用于有返回结果的任务。
  • ForkJoinPool :ForkJoinTask 需要通过 ForkJoinPool 来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。

image-20220304005022113

代码实例

task 类 里面编写的是我们继承了 递归任务继承的实现方法

public class forkjoinDemo extends RecursiveTask<Long> {
    /* 解决方案 也是有三六九等的,比如案例 求和
     * 最低等 就是直接for循环求和
     * 中等 使用forkjion
     * 高等 stream 并行流
     * */
//开始
    private long start;
    //结束
    private long end;
    //到多少值,才开始分开任务
    private long threshold = 10000L;

    public forkjoinDemo(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        //判断超过阈值的时候 开始使用 fork join
        if (end - start > threshold) {
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            //    求出中间值
            long mid = (start - end) / 2;
            forkjoinDemo task1 = new forkjoinDemo(start, mid);

            //拆分任务,把任务压入线程队列
            task1.fork();
            forkjoinDemo task2 = new forkjoinDemo(mid + 1, end);
            task2.fork();
            return task1.join() + task2.join();
        }
    }
}

测试类

三种方法的速度

public class test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //test1(); 7042;
        //test2(); 969
        //test3(); 179;
    }

    public static void test1() {
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (long i = 1L; i <= 10_0000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum" + sum + "=> 执行时间" + (end - start));
    }


    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new forkjoinDemo(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println("sum" + sum + "=> 执行时间" + (end - start));
    }

    public static void test3() {

        long start = System.currentTimeMillis();
        //并行流
        long reduce = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum" + reduce + "=> 执行时间" + (end - start));
    }
}

异步回调

什么是future

常见的两种创建线程的方式。一种是直接继承Thread,另外一种就是实现Runnable接口。

这两种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。

从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

Future模式的核心思想是能够让主线程将原来需要同步等待的这段时间用来做其他的事情。(因为可以异步获得执行结果,所以不用一直同步等待去获得执行结果)

image-20220304014056949

上图简单描述了不使用Future和使用Future的区别,不使用Future模式,主线程在invoke完一些耗时逻辑之后需要等待,这个耗时逻辑在实际应用中可能是一次RPC调用,可能是一个本地IO操作等。B图表达的是使用Future模式之后,我们主线程在invoke之后可以立即返回,去做其他的事情,回头再来看看刚才提交的invoke有没有结果。

Future接口的局限性

当我们得到包含结果的Future时,我们可以使用get方法等待线程完成并获取返回值,注意我加粗的地方,Future的get() 方法会阻塞主线程。即使我们使用isDone()方法轮询去查看线程执行状态,但是这样也非常浪费cpu资源。

image-20220304014316398

我们需要新的,更强大的拓展,CompletableFuture

在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,结合了Future的优点,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

CompletableFuture被设计在Java中进行异步编程。异步编程意味着在主线程之外创建一个独立的线程,与主线程分隔开,并在上面运行一个非阻塞的任务,然后通知主线程进展,成功或者失败。

通过这种方式,你的主线程不用为了任务的完成而阻塞/等待,你可以用主线程去并行执行其他的任务。 使用这种并行方式,极大地提升了程序的表现。

实例化:

有两种格式,一种是supply开头的方法,一种是run开头的方法

  • supply开头:这种方法,可以返回异步线程执行之后的结果
  • run开头:这种不会返回结果,就只是执行线程任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
获取结果

同步获取结果

public T    get()
public T    get(long timeout, TimeUnit unit)
public T    getNow(T valueIfAbsent)
public T    join()

简单的例子

CompletableFuture<Integer> future = new CompletableFuture<>();
Integer integer = future.get();

get() 方法同样会阻塞直到任务完成,上面的代码,主线程会一直阻塞,因为这种方式创建的future从未完成。有兴趣的小伙伴可以打个断点看看,状态会一直是not completed

代码使用案例

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        ////没有返回值的异步回调, runAsync
        //CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
        //    System.out.println(Thread.currentThread().getName() + "runAsync=> Void");
        //});
        //System.out.println("1111");
        ////获取执行结果
        //completableFuture.get();
     
     
        //    有返回值的
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "runAsync=>integer");
            int i = 10 / 0;
            return 1024;
        });
        completableFuture.whenComplete((t, u) -> {
            //t是正常的返回结果
            //u是返回报错信息
            System.out.println("t=>" + t);
            System.out.println("u=>" + u);
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233;
        }).get();
    }

Docker容器n8n中 read/write files from disk 无法读取容器内文件解决方案 《n8n文件读写Docker部署常见问题解决方案》摘要:针对n8n 1.90.2版本在Docker环境下文件读写失败问题,本文分析常见错误包括路径错误(ENOENT)、权限不足(EACCES)及节点误用问题。核心解决方案为:1)通过-v参数挂载宿主机目录到容器(如/files);2)使用新版Read/WriteFilesfromDisk节点操作文件;3)确保权限设置正确。提供可直接导入的示例工作流JSON,演示从CSV读取到结构化数据处理的全流程。特别强调已部署用户需重新挂载卷的正确操作方式,并附解决方案对 阅读详情

相关推荐

COMSOL模拟CO2驱替CH4:多物理场耦合建模与工程应用详解

在非常规天然气开发与碳封存工程中,多孔介质内的渗流、吸附与岩石变形相互耦合,使得CO2驱替CH4的产能预测和CO2封存评估无法仅靠经验公式完成。以达西定律描述裂隙流动、以扩展Langmuir方程刻画竞争吸附、用应力-吸附-渗透率关系关联煤岩变形,是多物理场耦合数值模拟的核心路径。COMSOL Multiphysics凭借内置渗流、固体力学与组分传递接口,能够将这些机制整合为可参数化扫描的仿真模型,为注气压力、井距、渗透率演化等方案设计提供量化依据。围绕CO2驱替CH4模拟的建模流程、参数取法与结果判读,可形

weixin_29574585的博客 158

ForkJoin详解

Fork/Join 是一个分而治之的任务框架,如一个任务需要多线程执行,分割成很多块计算的时候,可以采用这种方法。 动态规范:和分而治之不同的是,每个小任务之间互相联系。 工作密取:分而治之分割了每个任务之后,某个线程提前完成了任务,就会去其他线程偷取任务来完成,加快执行效率。同时,第一个分配的线程是从队列中的头部拿任务,当完成任务的线程去其他队列拿任务的时候是从尾部拿任务,所以这样就避免了竞争。 在Java的Fork/Join框架中,使用两个类完成上述操作:   1.ForkJoinTask:我们要使用F

baihaiyang99的博客 1万+

ArcGIS Pro中如何批量替换数据源

不知您是否遇到过下面情况:ArcGIS Pro的Map/Scene中所引用的数据源被移动或重命名了,导致Map/Scene中找不到之前的数据,出现红色叹号;已有一个Map模板文件,图层的样式符号都是设置好的,想批量设置成另外一个数据源,复用该模板文件中的样式符号。 如果您在ArcGIS Pro的使用过程中遇到了上面情况,那么可以参考本篇文章,本文以图层存储在同一GDB中为例介绍。 情况一:Map中已有图层的数据源被移动了 原始Map长这样: 当数据源位置改变之后,显示找不到数据源的红色叹号: 这时如果右

xinying180的博客 7945

Fork/Join分支合并框架、异步回调——JUC随记9

1、概述 从JDK1.7开始,Java提供Fork/Join框架用于并行执行任务,它的思想就是讲一个大任务分割成若干小任务,最终汇总每个小任务的结果得到这个大任务的结果。 这种思想和MapReduce很像(input --> split --> map --> reduce --> output) 主要有两步: 第一、任务切分; 第二、结果合并; 2、示例 import java.util.concurrent.ExecutionException; import java.util

码说芯语的博客 259

锐捷 MPLS VPN 配置

ISP内部配置 , PE设备和CE设备之间的接口不开启 ( 只在物理接口上配置 , loopback 口不配置 )最后一步关闭 ipv4 单播 , 如果你发现路由奇奇怪怪的 , 可以尝试一下。both --> 同时配置 import export 值。绑定后接口会重置 , 且接口的路由会转到VRF路由表中。这里基本上绑定了 vrf 实例的接口都需要宣告进去。vpnv 4 通道是用来传递 mpls 路由的。先建立邻居管理,建立VPNV4通道。这里要做双向引入 , 才会有路由。export 导出值。

dekangzou的博客 1710

rk3588主板PDM接口接数字MIC录音

本文介绍了Rockchip 3588平台上6麦克风PDM接口的硬件连接与软件调试过程。硬件方面展示了PDM0_CLK0_M0时钟脚和三组数据脚的连接方案。软件配置包括设备树修改,生成声卡节点,并提供了6通道录音命令示例。文章重点分析了两个常见问题:录音无声(需检查供电、接线和波形)和通道数据缺失(需正确配置rockchip,path-map寄存器映射)。最后通过Audition工具验证录音文件,确认各通道工作正常。调试过程完整展现了从硬件连接到软件配置的完整流程。

m0_60805802的博客 1011

fork-join详解(2)

4.fork-join实战异步读取磁盘文件 本次采用execute异步来启动ForkJoinPool,小伙伴们可以看启动结果跟invoke启动的区别 具体思路: 1.首先拿到我当前这个磁盘下的所有文件遍历循环 2.循环中判断文件是否是目录,如果是目录再次使用递归任务进行任务添加 3.如果不是目录就输出文件路径 4.对添加到目录递归任务集合中的数据再次进行递归 具体代码: public class main { //读取文件任务对象 static class FileTa

qfzhaohan的博客 1581

基于PChmi.dll的PLC报警显示和记录(C#)

本文介绍了如何使用C#编写基于PChmi.dll的程序,实现PLC报警的显示和记录功能。通过订阅PLC的报警事件,我们可以实时显示报警信息,并通过日志文件记录报警历史。PLC(可编程逻辑控制器)在工业自动化领域中扮演着重要的角色,而报警显示和记录是PLC应用中常见的需求。本文将介绍如何使用C#编写基于PChmi.dll的程序,实现PLC报警的显示和记录功能。当然,这只是实现报警显示和记录的基本框架,具体的实现方式可能因不同的PLC和HMI库而有所差异。你需要根据你所使用的具体库和设备进行相应的调整和修改。

ByteLegend的博客 1669

Java 并发编程(五)-线程池一

一、ThreadPool线程池 1、概述 线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销。进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。 线程池的优势:线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中

杀神lwz的博客 1439

STM32实战开发(110):基于STM32的智能地震监测系统开发

地震监测系统是自然灾害预警体系中至关重要的一部分。随着地震灾害的频发和对自然灾害预测精度要求的提高,智能地震监测系统逐渐成为研究的热点。STM32作为一款高性能的微控制器,凭借其强大的处理能力和丰富的外设接口,在地震监测领域中得到了广泛应用。本篇文章将详细介绍如何使用STM32实现一个智能地震监测系统的开发过程,涵盖硬件设计、软件编程、系统实现及相关代码。通过集成加速度传感器、地震数据采集、实时数据处理和无线数据传输功能,我们可以设计一个高效、稳定的智能地震监测系统。

2201_76125393的博客 710

Vue3 - 手机移动端H5网页强力解决弹起软键盘后页面被挤压变形或页面整体上移问题,点击input输入框键盘弹出底部被上推往上顶遮挡及各种问题(兼容安卓苹果ios手机,微信公众号网页微信内置浏览器)

vue3,h5网页,input输入框组件,vue3 h5软键盘弹出,vue3输入框,键盘弹起时页面整体上移问题,uni,H5页面,解决软键盘弹起页面被挤压、被往顶的问题,解决vue3输入框,键盘弹起时页面整体上移问题,vue3调用手机系统软键盘,页面窗口被顶起,布局定位失效,解决VUE3软键盘弹起导致页面fixed定位元素被顶上去,输入法软键盘弹起,窗口顶起,变形解决办法,Vue中input组件在IOS设备上弹出软键盘时页面整体上移问题的解决方案,以及input组件聚焦后弹出软键盘固定在,Vue3软键盘把界

🏆 前端领域优质创作者 1万+

JUC(11)ForkJion与异步回调实列

ForkJIon 1.创建任务 /** * 求和计算的任务! * */ public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; // 1 private Long end; // 1990900000 // 临界值 private Long temp = 10000L; public ForkJoinDemo(Long start, Long end) { this.start = start; t

OVO_LQ_Start的博客 249

Django 6.0:有哪些新特性以及它们对实际项目的重要性

Django 6.0 感觉像是一个成熟的版本,它基于实际应用案例而打造。它减少了不必要的复杂性,同时扩展了框架的开箱即用功能。升级到 Django 6.0 不仅仅是为了保持版本更新,更是为了构建更易于维护、更安全、更能适应未来发展的应用程序。

iCloudEnd的博客 338

什么是ForkJoin

ForkJoin

袁的博客 2180

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

精雕软件(JDpaint) 5.5 绿色版

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

verilog中的fork...join用法

这句话通常使用在验证之中,也就是常说的system verilog(SV),写在testbench,不可综合。如果在功能代码中这样写进程,或者显示的写延时语句是没有用的,只能用于功能查看,无法综合这些指定的内容。但是功能代码中常常使用阻塞与非阻塞进行流水线设计,以及多个模块的并行。像testbench中还通常会有任务task,函数func,打印,以及其他特殊约束或者规则等。 总之,verilo...

edward_zcl的博客 6万+

Statistics and Machine Learning Toolbox

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

System Verilog线程——fork join的理解使用

本文参考绿皮书第七章,线程及其线程间的通信,Verilog HDL A guide to digital design and synthesis 2nd第七章。主要对于begin…end,fork…join等进行简单总结。然后给出相对应的实例进行理解。有问题欢迎同学指出。 System Verilog线程——fork join的理解使用概述顺序块与并发块(Sequential and Parallel Blocks)begin...end块fork...join块fork...join_any与for.

啦啦啦啦啦 1万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

【并发编程篇】详解Forkjoin

Fork/Join 是一种在多线程领域中常用的算法或技术,它的核心思想是将大任务分割成若干个小任务,然后将这些小任务分配给多个线程并行处理,最终将结果合并起来。这种思想可以应用于多种场景,例如图像处理、批处理、并行排序等。在 Java 中,Fork/Join 这种思想被封装在了 java.util.concurrent 包中的 ForkJoinPool 类和 RecursiveTask 类中。

小吉妙妙屋 2473

Fork/join框架

1 Frok/Join框架简介 Fork/Join框架是Java7提供了的一个用于并行执行的任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。 Fork 递归地将任务分解为较小的独立子任务,直到它们足够简单以便异步执行。 Join 将所有子任务的结果递归地连接成单个结果,或者在返回void的任务的情况下,程序只是等待每个子任务执行完毕。 比如计算 1+2+......+10000,可以分割成10个子任务,每个子任务对1000个数进行求和,最终汇

leimeng123的博客 3934

systemverilog在for循环中使用fork_join和fork_join_none的区别

我想在一个for循环中fork_join或者fork_join_none语句实现多线程, 假如我使用经典的方法:for(int index=0;index&lt;14;index++)begin automatic int idx=index; fork begin `uvm_do_on(sequence_inst,p_sequencer.m...

XtremeDV 2万+

系统结构-并行算法FORK JOIN

并行算法FORK JOIN一、FORK JOIN定义二、举例题目分析: 一、FORK JOIN定义 FORK语句的形式: FORK m,其中m为新进程开始的标号。 执行FORK m语句时,派生出标号为m开始的新进程,具体为: 1、准备好这个新进程启动和执行所必需的信息; 2、如果是共享主存,则产生存储器指针、映像函数和访问权数据; 3、将空闲的处理机分配给派生的新进程,如果没有空闲处理机,则让它们排队等待; 4、继续在原处理机上执行FORK语句的原进程。 与FORK语句相配合,作为每个并发进程的终端语句J

Hogwarts 6782

fork join、fork join_any、fork join_none的区别

SV基础

m0_60738037的博客 4977

什么是ForkJoin、ForkJoin分支合并、ForkJoin工作窃取、ForkJoin大数据求和计算

什么是ForkJoin、ForkJoin分支合并、ForkJoin工作窃取、ForkJoin大数据求和计算什么是ForkJoin?ForkJoin:分支合并ForkJoin特点:工作窃取如何让使用ForkJoinForkJoin求和计算Demo 什么是ForkJoin? ForkJoin(分支合并)是jdk1.7之后出来的,并行执行任务,提高效率,用在大数据量场景下。 大数据:Map Reduce(把大任务拆分成多个小任务,怎么拆分用到了二分算法),每个小任务得出自己的结果,之后再把结果汇总,汇总的过程就是

郝开的博客 1159

ForkJoinTask的异常处理机制和方法

ForkJoinTask类提供了以下两个方法: public final boolean isCompletedAbnormally() { return status < NORMAL; } 以上方法其实就是做个任务状态的判断,如果任务抛出了异常,或者被取消,则返回true。 public final Throwable getException() { int s = status & DONE_MASK; return ((s >= NORMAL)

硅脂味咖啡的博客 1514

ForkJoin源码分析

一、什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。 我们再通过Fork和Join这两个单词来理解下Fork/Join框架,Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。比如计算1+2+。。+10...

tangyb828的博客 564

Fork/Join框架简介

1 引子 Fork/Join框架是从Java1.7开始提供的一个并行处理任务的框架(本篇博客基于JDK1.8分析),它的基本思路是将一个大任务分解成若干个小任务,并行处理多个小任务,最后再汇总合并这些小任务的结果便可得到原来的大任务结果。 从字面意思来理解Fork/Join框架,"Fork"表示“分叉”,它把大任务分解成多个小任务并行处理,而“Join”表示“联合”,它将这些小任务的结果...

小伍的程序之路 748

systemverilog中的for和fork join嵌套

sv中使用 fork join 来同时挂起多个线程。当需要同时挂起N个线程时,显然一个个挂起显得有些麻烦,而且代码不太雅观。本文章主要阐述sv中 for 和 fork join 的嵌套编程,以及遇到的问题和分析。利用for嵌套fork join_none,同时挂起多个线程,在这里task proc一定要加automatic,这是因为sv中任务默认是静态的,如果你试图在测试程序里的多个地方调用同一任务,由于任务里的局部变量会使用共享的静态存储区,所以不同的线程之间会窜用这些变量。

weixin_42875066的博客 1269
上一篇: JUC系列(六) 线程池
下一篇: JUC系列(八)Java内存模型 volatile关键字与单例模式实践
冷环渊
博客等级 码龄7年 1130粉丝 176原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冷环渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值