Java8新特性 CompletableFuture

【Java】Java8新特性:CompletableFuture CompletableFuture Java CompletableFuture 详解 阅读详情

Java8新特性 CompletableFuture

什么是CompletableFuture?

CompletableFuture类的设计灵感来自于 Google Guava 的 ListenableFuture 类,它实现了 Future 和 CompletionStage 接口并且新增了许多方法,它支持 lambda表达式,通过回调利用非阻塞方法,提升了异步编程模型。它允许我们通过在与主应用程序线程不同的线程上(也就是异步)运行任务,并向主线程通知任务的进度、完成或失败,来编写非阻塞代码。

在这里插入图片描述

为什么引入CompletableFuture?

Java 的 1.5 版本引入了 Future,你可以把它简单的理解为运算结果的占位符,它提供了两个方法来获取运算结果。

  • get():调用该方法线程将会无限期等待运算结果。
  • get(long timeout, TimeUnit unit):调用该方法线程将仅在指定时间 timeout 内等待结果,如果等待超时就会抛出 TimeoutException 异常。

Future 可以使用 Runnable 或 Callable 实例来完成提交的任务,通过其源码可以看出,它存在如下几个问题:

  • 阻塞 调用 get() 方法会一直阻塞,直到等待直到计算完成,它没有提供任何方法可以在完成时通知,同时也不具有附加回调函数的功能。
  • 链式调用和结果聚合处理 在很多时候我们想链接多个 Future 来完成耗时较长的计算,此时需要合并结果并将结果发送到另一个任务中,该接口很难完成这种处理。
  • 异常处理 Future 没有提供任何异常处理的方式。

而我们的CompletableFuture则成功地解决了上述的这些问题,下面将一一介绍CompletableFuture的一些常用的API方法的使用:

CompletableFuture的API

首先CompletableFuture实现Future接口,故Future接口存在的常见方法它本身也存在,这里不再进行讲解,处理以外,它还提供了手动完成complete()方法,判断是否完成isDon()方法,取消执行cancel()等等。

1. 异步执行方法 runAsync/supplyAsync

runAsync接受的参数为Runnable参数,无返回值;而supplyAsync接受的参数则为Supplier supplier,即有返回值的函数式接口,可以参考下表回顾一下函数式接口:

在这里插入图片描述

故我们简单编写一个方法,分别使用这两种异步任务的调用方式:

public class TestCompletableFuture {
    public static void main(String[] args) {
      	//runAysnc
        testRunAsync();
      	//supplyAsync
        testSupplyAsync();
    }
    
    /**
     * 异步执行Code 无返回值
     */
    private static void testRunAsync() {
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("runAsync...");
        });
    }

    /**
     * 异步执行Code 有返回值
     */
    private static void testSupplyAsync() {
        CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "supplyAsync...";
        });
        try {
            //异步获取结果
            String result = stringCompletableFuture.get();
            System.out.println("result:" + result);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }
}

输出结果:

runAsync...
result:supplyAsync...

2. 完成执行 whenComplete/whenCompleteAsync

当我们定义上面的runAsync或者supplyAsync方法执行完毕以后,可以调用whenComplete或者whenCompleteAsync继续执行任务,区别在于:

  • whenComplete是有之前执行异步任务的线程继续执行任务,如果定义多个whenComplete,则它们之间是链式串行调用的关系
  • whenCompleteAsync则是将任务提交到线程池中进行执行,如果定义多个whenCompleteAsync,则它们之间则是并行调用的关系

首先我们来看whenComplete的使用方法:

main() {
  testWhenComplete();
}
private static void testWhenComplete() {
  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello World");
  //定义两个whenComplete任务,此时会先执行01,再执行02
  future.whenComplete((res, error) -> {
    try {
      Thread.sleep(5000);
      System.out.println("whenComplete01");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  future.whenComplete((res, error) -> {
    try {
      Thread.sleep(1000);
      System.out.println("whenComplete02");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  System.out.println("completed...");
}

输出结果:

//sleep 5s
whenComplete01
//sleep 1s  
whenComplete02
completed...

然后我们再来看whenCompleteAsync的使用方法:

main() {
  testWhenCompleteAsync();
}

private static void testWhenCompleteAsync() {
  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello World");
  //此时定义两个whenCompleteAsync,在future执行完毕以后,两者并发执行
  future.whenCompleteAsync((res, error) -> {
    try {
      Thread.sleep(5000);
      System.out.println("whenComplete01");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  
  future.whenCompleteAsync((res, error) -> {
    try {
      Thread.sleep(1000);
      System.out.println("whenComplete02");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  
  System.out.println("completed...");
  try {
    Thread.sleep(10000);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

输出结果:

completed...
//sleep 1s  
whenComplete02
//sleep 4s  
whenComplete01

3. 链式执行任务 thenCompose/thenApply

两者均可将CompletableFuture连接起来,但是存在一些差异:

  • thenApply()接收的是前一个调用返回的结果,然后对该结果进行处理。
  • thenCompose()接收的是前一个调用的stage,返回flat之后的的CompletableFuture,即将上一个Future的执行结果作为下一个Future的输入

thenApply方法:

main() {
  testThenApply();
}
private static void testThenApply() {
  CompletableFuture<String> completableFuture = CompletableFuture
    .supplyAsync(() -> "Hello")
    //s为上个任务的结果,可对其进行处理
    .thenApply(s -> s + "World");
  try {
    String result = completableFuture.get();
    System.out.println(result);
  } catch (InterruptedException e) {
    e.printStackTrace();
  } catch (ExecutionException e) {
    e.printStackTrace();
  }
}

输出结果:

HelloWorld

thenCompose方法:

main(){
  testThenCompose();
}
private static void testThenCompose() {
  CompletableFuture<String> stringCompletableFuture = CompletableFuture
    .supplyAsync(() -> "Hello")
    //s即为上一个CompletableFuture的输出结果
    .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "World"));
  try {
    String result = stringCompletableFuture.get();
    System.out.println(result);
  } catch (InterruptedException e) {
    e.printStackTrace();
  } catch (ExecutionException e) {
    e.printStackTrace();
  }
}

输出结果:

HelloWorld

4. 合并处理 thenCombine/thenAcceptBoth

两个合并的方法本质上的区别在于thenCombine有返回值,而thenAcceptBoth无返回值

public class TestCompletableFuture {
    public static void main(String[] args) {
        //有返回值的合并操作
        testThenCombine();
        //无返回值的合并操作
        testThenAcceptBoth();
    }

    private static void testThenAcceptBoth() {
        CompletableFuture.supplyAsync(() -> "Hello")
                .thenAcceptBoth(
                    CompletableFuture.supplyAsync(() -> "World"), (s1, s2) -> System.out.println(s1 + s2)
        );
    }

    private static void testThenCombine() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello")
                .thenCombine(
                        CompletableFuture.supplyAsync(() -> "World"), (c1, c2) -> c1 + c2
                );
        try {
            String result = completableFuture.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

HelloWorld
HelloWorld

5. 聚合处理 allOf/anyOf

我们需要并行执行任务时,通常我们需要等待所有的任务都执行完毕再去处理其他的任务,那么我们可以用到allOf,等同于 CountDownLatch闭锁。而anyOf则是任务中有一个完成则直接去处理其他的任务,无需再等待其他任务执行完毕。

allOf方法:

public class TestCompletableFuture {
    public static void main(String[] args) {
        testAllOf();
    }

    private static void testAllOf() {
        //定义三个任务
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> System.out.println("task1 running"));
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> System.out.println("task2 running"));
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() ->
        {
            try {
                //模拟延迟任务
                Thread.sleep(5000);
                System.out.println("task3 running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //合并三个任务
        CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2, future3);
        System.out.println("task waiting...");
        //等待所有任务执行完毕
        future.join();
        System.out.println("task completed");
    }
}

输出结果:

task1 running
task2 running
task waiting...
//waiting about 5s
task3 running
task completed

anyOf方法:

public class TestCompletableFuture {
    public static void main(String[] args) {
        testAnyOf();
    }

    private static void testAnyOf() {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(
                () -> {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("task1 running...");
                }
        );
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(
                () -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("task2 running...");
                }
        );
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(
                () -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("task3 running...");
                }
        );
        //只要有一个任务执行完毕,即不再等待...
        CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(future1, future2, future3);
        System.out.println("task waiting...");
        completableFuture.join();
        System.out.println("task completed");
    }
}

输出结果:任务2完毕继续其他任务,不再等待任务1和任务3

task waiting...
task2 running...
task completed

6. 多任务返回值联合处理 join

public class TestCompletableFuture {
    public static void main(String[] args) {
        testJoin();
    }

    private static void testJoin() {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Future");
        //对三个任务的返回值进行join处理
        String result = Stream.of(future1, future2, future3)
                .map(CompletableFuture::join)
                .collect(Collectors.joining(" "));
        System.out.println(result);
    }
}

输出结果:

Hello World Future

7. 异常处理 handle(result, exception)

我们在异步任务过程中可以抛出异常,并通过handle进行异常处理:

public class TestCompletableFuture {
    public static void main(String[] args) {
        testHandle();
    }

    /**
     * 异常处理
     */
    private static void testHandle() {
        //模拟参数为null
        String param = null;
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            if (param == null) {
                throw new RuntimeException("params_error");
            }
            return "Hello" + param;
        }).handle((res, ex) -> res != null ? res : ex.getMessage());

        try {
            String result = completableFuture.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

以上就是CompletableFuture的常见的一些API,当然还有很多其他一些API方法,这里不再赘述,可以查看对应的API文档进行使用…

java8--新特性CompletableFuture多线程 java8新特性CompletableFuture多线程 参考文章: http://javadaily.cn/articles/2021/03/18/1616064645189.html https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650 CompletableFuture可以指定异步处理流程: thenAccept()处理正常结果; exceptional()处理异常结果; thenApplyAsync()用于串行化另一个C 阅读详情

相关推荐

Java 8的新特性:Lambdas和CompletableFuture解析

1.背景介绍 Java 8是Java语言的一个重要版本,它引入了许多新的特性,这些特性使得Java语言更加强大和灵活。其中,Lambdas和CompletableFuture是Java 8最重要的两个新特性之一。Lambdas是Java 8中引入的一种新的函数式编程特性,它使得Java代码更加简洁和易读。CompletableFuture是Java 8

AI天才研究院 1046

Java 8 的异步编程利器 CompletableFuture 详解

文章目录一个例子回顾 Future一个例子走进CompletableFutureCompletableFuture使用场景创建异步任务supplyAsync方法runAsync方法任务异步回调1. thenRun/thenRunAsync2.thenAccept/thenAcceptAsync3. thenApply/thenApplyAsync4. exceptionally5. whenComplete方法6. handle方法多个任务组合处理AND组合关系OR组合的关系AllOfAnyOfthenCo

ThinkWon的博客 5831

5分钟搞定SNMP工具安装:从Linux到Windows的snmpwalk/snmpget全攻略

本文提供了SNMP工具(snmpwalk/snmpget)在Linux和Windows系统上的快速安装与使用指南,涵盖从基础安装到高阶应用的全面解析。通过详细的命令示例和故障排查技巧,帮助用户快速掌握网络设备监控与诊断的核心技能,提升运维效率。

weixin_29248313的博客 555

Java 8 与并发(一)

一、Java 8 的函数式编程 1.1 函数作为一等公民 JavaScript被称之为多范式语言,你既可以把它当做面向对象的语言也可以当做函数式语言。在jQuery中经常可以看到如下的代码: $("button").click(function(){ $("li").each(function(){ alert($(this).text()); }); }); 注意这里each(...

AaronSimon的博客 753

DINOv2 基于自定义数据集进行图片分类预测

(前提是安装好pytorch)1. pip安装2. 下载dinov2提供的预训练模型,需要自行前往网站上查找dinov2模型进行下载。模型需要下载三个文件:下载完成后将上述三个文件放置在本地一个名为dinov2_base的文件夹中即可。

sinat_34770838的博客 6665

绕过卡巴斯基通过RPC控制lsass注入DLL

参考国外的XPN以及3gstudent大佬的文章: https://blog.youkuaiyun.com/xiangshen1990/article/details/104872566 https://gist.github.com/xpn/93f2b75bf086baf2c388b2ddd50fb5d0 原理:正常的ssp扩展(dll)可以加载到lsass进程中去,比如kerberos验证都是通...

2ed 3524

精通 Java8 并发编程(二)

原文:zh.annas-archive.org/md5/BFECC9856BE4118734A8147A2EEBA11A 译者:飞龙 协议:CC BY-NC-SA 4.0 第三章:从执行者中获得最大效益 在第二章中,管理大量线程-执行者,我们介绍了执行者的基本特性,作为改进执行大量并发任务的并发应用程序性能的一种方式。在本章中,我们将进一步解释执行者的高级特性,使它们成为您并发应用程序的强大工具。在本章中,我们将涵盖以下内容: 执行者的高级特性 第一个例子-高级服务器应用程序 第二个例子-执

龙哥盟 741

避开这些坑!医疗影像数据增强的6个安全操作指南(附PyTorch代码)

本文详细介绍了医疗影像数据增强的六大安全准则,包括解剖结构守恒、密度值物理意义保留和病灶特征保护等关键原则,并提供了PyTorch实战代码。通过具体案例和参数建议,帮助开发者在肺结节检测和MRI分割等医疗AI应用中实现安全有效的数据增强,避免因不当操作导致的诊断误差。

cola5的博客 555

Java8新特性 之CompletableFuture

相比于JDK5所提出的future概念,future在执行的时候支持异步处理,但是在回调的过程中依旧是难免会遇到需要等待的情况。 在JDK8里面,出现了CompletableFuture的新概念,支持对于异步处理完成任务之后自行处理数据。当发生异常的时候也能按照自定义的逻辑来处理。 创建一个异步任务 Async结尾的方法都是可以异步执行的,如果指定了线程池,会在指定的线程池中执行,如果没有指定,默...

const_ 2077

2025美赛E题为农业留出空间(附代码+全保姆教程)Making Room for Agriculture

有机农业的核心在于减少化学投入,促进自然控制和可持续性。变量定义CCC: 作物种群III: 害虫种群PPP: 天然捕食者种群(蝙蝠、鸟类等)BBB: 生物多样性水平(以种类数量表示)SSS: 土壤健康指数(影响植物繁殖与作物生长)EEE: 农业生态系统的整体稳定性。

博客 1495

从权限对象到业务角色:把 SAP Authorization Object 讲清楚,并用 ABAP 写对一次 AUTHORITY-CHECK

摘要:SAP权限管理的核心在于Authorization Object(授权对象),它通过组合最多10个授权字段来精确控制用户访问权限。关键要点包括:1) 授权对象与ACTVT字段(如01创建/02修改/03显示)的配合使用;2) ABAP中AUTHORITY-CHECK语句的四种常见返回码(0通过/4字段不匹配/12无权限/40无效用户);3) 系统自动执行的S_TCODE事务码检查;4) 自定义授权对象时应遵循"最少且足够"原则,优先复用标准对象。在RAP和OData场景中,权限检查

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 265

Java8异步利器:CompletableFuture全网最全使用教程

点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!每天14:00更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 Elast...

weixin_44421461的博客 1458

零基础学GPU KMD--8. 内存管理:GPU 显存与系统内存的协同​8.1 显存分配策略:GEM / TTM 原理

支持 VRAM、GTT(Graphics Translation Table)、SYSTEM 内存的动态迁移与缓存管理。:若缓冲区在 SYSTEM,TTM 分配 GTT 条目并建立页表映射,使 GPU 可直接访问。:系统内存中预留给 GPU 直接访问的区域,通过 GPU 的 MMU 做地址翻译。你需要我继续展开这一部分吗?​ 为复杂离散 GPU 设计,应对大容量 VRAM、多内存域、显存迁移等问题。:重量级、功能全,适合大容量、多内存域的离散 GPU,强调显存迁移与统一管理。

amy_mhd的博客 185

Java8新特性之CompletableFuture

书籍原文:https://tuonioooo.gitbooks.io/java-concurrent/content/javautilconcurrentbao-jiang-jie/java8xin-te-xing-zhi-completablefuture.html

tuonioooo 279

运维系列【仅供参考】:RustDesk部署疑难解答

RustDesk部署疑难解答

weixin_54626591的博客 279

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Java8新特性CompletableFuture详解

thenCombine()被用来当两个独立的Future都完成的时候,用来做一些事情。CompletableFuture 是Java 8 中引入的 Java Future API的扩展,用于 Java 中的异步编程,它可以使我们的任务运行在与主线程分离的其他线程中,并通过回调在主线程中得到异步任务执行状态,包括是否完成,是否异常等信息。我们 使用 thenCompose()组合两个独立的future,假设我们想从一个远程API中获取一个用户的信息,当用户信息可用时,我们想从另外一个服务中获取另外的信息。

qq_42077317的博客 1927

Statistics and Machine Learning Toolbox

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

【Java 8 新特性】Java CompletableFuture supplyAsync()详解

【Java 8 新特性】Java CompletableFuture supplyAsync()

qq_45721579的博客 8637

IDEA的使用指南

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

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

【Java 8 新特性】Java CompletableFuture supplyAsync()

Java CompletableFuture supplyAsync与 thenApply() 一起使用自定义执行器 Executor与 whenComplete() 一起使用与流(Stream)一起使用参考文献 在此页面上,我们将提供Java CompletableFuture.supplyAsync()示例。 supplyAsync()是Java 8中引入的CompletableFuture静态方法。 方法supplyAsync()完成在ForkJoinPool.commonPool()或给定的Exec

猫巳的博客 3万+

【Java 8 新特性】Java 8 CompletableFuture 示例

Java 8 CompletableFuture 示例CompletableFuture.supplyAsyncCompletableFuture.thenApplyCompletableFuture.joinCompletableFuture.thenAcceptCompletableFutureTwoExample.javaCompletableFuture.whenCompleteCompletableFuture.getNow参考文献 java.util.concurrent.Completable

猫巳的博客 1538

Java8新特性整理之CompletableFuture

Java8新特性整理之CompletableFuture Future 接口的局限性 Future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性。实际开发中,我们经常需要达成以下目的: 将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果。 等待 Future 集合中的所有任务都完成。 仅等待 Future集合中最快结束...

weixin_42040639的博客 413

java1.8新特性 CompletableFuture 使用案例详解

CompletableFuture方法详解 1、runAsync 和 supplyAsync方法 CompletableFuture 提供了四个静态方法来创建一个异步操作。 public static CompletableFuture<Void> runAsync(Runnable runnable); public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);.

李虹柏的博客 1396

Java8新特性之CompletableFuture详解

jdk1.8中加入了CompletableFuture类来辅助实现异步编程。上一节中提到了线程池执行异步方法返回Fufure实例,但是当执行多个异步任务时,如何判断这些任务都已执行完毕则没有好的办法,只能通过定时轮询去判断这些任务isDone(),并且也需要创建线程池来实现。而新的CompletableFufure类则对异步编程提供了一些很不错的方法。如下: 静态方法: C

fw118958的专栏 1万+

Java8的新特性——Stream与completableFuture详解

具体来说,Fork/Join 框架使用了一个工作窃取算法,当某个线程完成任务时,它可以“窃取”其他线程的任务来处理。当任务完成时,无法直接通知主线程,这会导致回调地狱的问题。如果需要在任务完成后执行某些操作,通常需要在主线程中进行轮询,检查任务是否完成,这种方法繁琐且不优雅。时,我们可以轻松定义任务完成后的操作,而无需阻塞当前线程,提升了代码的效率和可维护性。来说,提交的任务在一个独立的线程中执行,主线程可以继续工作,而不是被阻塞。的一个扩展,提供了更强大的功能,允许我们以非阻塞的方式处理异步计算结果。

m0_53926113的博客 1528

【Java 8 新特性】Java CompletableFuture thenApply()

Java CompletableFuture继承了CompletionStage和Future接口。 CompletableFuture.thenApply继承自CompletionStage。 thenApply返回一个新的CompletionStage,当该阶段正常完成时,将使用该阶段的结果作为所提供函数的参数来执行该过程。 从Java文档中找到thenApply的方法声明。 <U> CompletionStage<U> thenApply(Function<? super

猫巳的博客 7170

【Java8新特性--->异步处理】CompletableFuture

【Java1.8新特性--->异步处理】ComplatableFuture

qq_52793248的博客 5132
上一篇: SpringCache入门
下一篇: 提交本地项目至码云(gitee)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值