JAVA并发编程笔记(二)---Executor框架

Java并发编程实战:深入探索线程池与Executor框架 线程池是一种基于池化思想管理线程的资源管理技术。它预先创建并维护一定数量的线程,当有任务到来时,直接从线程池中获取空闲线程执行任务,而不是每次都创建新线程。降低资源消耗:减少了线程的创建和销毁开销。提高响应速度:由于线程池中的线程已经预先创建,可以快速响应任务请求。提高系统吞吐量:通过复用线程,系统能够同时处理更多任务。Java的线程池与Executor框架为高效并发编程提供了强有力的支持。通过合理配置线程池参数、正确提交和处理任务,可以显著提升系统的性能和稳定性。 阅读详情

一:简介

public interface Executor {
    void execute(Runnable command);
}

Executor是一个灵活而且强大的框架的基础,可以用于异步任务执行,而且支持很多不同类型的任务执行策略。它还为任务提交和任务执行之间的解耦提供了标准方法。为使用Runnable描述任务提供了通用的方式。Executor的实现提供了对生命周期的支持以及钩子函数,可以添加统计收集、应用程序管理机制和监视器等扩展。

Executor基于生产者-消费者模式。提交任务的执行者是生产者,执行任务的线程是消费者,使用Executor设计生产者-消费者模式最简单。

当你想为任务制定执行策略时,Executor更加灵活和适用。执行策略指明了:任务在什么线程中执行,以什么顺序执行(FIFO、LIFO、优先级),有多少任务并发执行,有多少个任务可以进入等待执行队列等等。

二:线程池

 线程池管理一个工作者线程的同构池(就是一个池子里面放的是各个线程),它的优势是每次任务过来不用重新创建线程,直接从池子里获取,节省了一定得开销,合适大小的线程池能大大提高响应性。

Executors提供了多种类型线程池的创建。

public class Executors {
    //创建定长的线程池 没提交一个任务就创建一个线程 直至到达最大值保持不变
    //(一个线程由于非预期异常结束,会补充一条新的线程)
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
    //创建一个可缓存的线程池 如果当前长度超过处理的需要时,可以灵活回收空闲线程,
    //需求增加时,灵活添加新线程,不会对池子的长度做任何限制。
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
    //创建一个单线程的Executor,它只创建唯一的工作者线程来执行任务,如有异常会有另外一个线程替代
    //它会保证依照任务队列规定的顺序执行。
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
    //创建一个定长线程池,支持定时的以及周期性的任务执行
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

}

三:Executors的生命周期

Executor实现通常只是为执行任务而创建线程,但JVM会在所有线程都终止后才退出,如果无法正确关闭Executor,将会阻止JVM的结束。因为Executor是异步地执行任务,所以在任何时间里,所有之前提交的任务的状态都是不能立即可见的。为了解决这个问题,ExecutorService扩展了Executor并且添加了一些用于生命周期管理的方法。

public interface ExecutorService extends Executor {
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;    
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;    
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

ExecutorService暗示生命周期的三种状态,运行,关闭,终止。ExecutorService创建后的初始状态是运行状态,shutdow方法会启用一个平缓的关闭过程:停止接收新线程,同时等待已经提交的任务完成(包括尚未执行的任务)。shutdowNow方法启用一个强制的关闭过程:尝试取消所有运行中的任务和在队列中尚未开始的任务。

在关闭后提交到ExecutorService中的任务,会被拒绝执行处理器处理(Rejected Execution Handler),拒绝执行处理器可能只是简单的放弃任务,也可能会引起executor抛出一个未检查的RejectedExecutionException。

四:可携带结果的任务:Callable和Future

  Executor框架使用Runnable作为其任务的基本表达形式,Runnable不能返回一个值或者抛出受检查的异常。很多任务都会引起严重的计算延迟,对于这样的任务Callable是最佳的抽象,它在进入主进入点-call方法等待返回值,并为可能抛出的异常预先做好了准备。

@FunctionalInterface
public interface Callable<V> {
   
    V call() throws Exception;
}

Future描述了任务的生命周期,并提供了相关的方法来获取任务的结果,取消任务以及检验任务是否已经完成还是取消。如果任务没有完成,get会阻塞直到它完成。如果任务抛出异常,get方法会将异常封装成ExecutionException,然后重新抛出;如果任务取消,get方法会抛出CancellationException。

ExecutorService中所有的submit方法都返回一个Future,因此可以将Runnable或Callable提交给executor,然后得到一个Future,用它来重新获取任务执行的结果,或者取消任务。也可以显示为Runnable或Callable实例化一个FutureTask(FutureTask实现了Runnable,所以既可以将它交给Executor执行,又可以直接调用run方法运行)。

五:CompletionService

如果你向Executor提交了一个批处理任务,并且希望在他们完成后获得结果。这个时候可以使用完成服务(Completion Service),CompletionService整合了Executor和BlockingQueue的功能,你可以将Callable任务交给它执行,然后使用类似于队列中的take和poll方法,在结果完整可用时获得这个结果,

ExecutorCompletionService是CompletionService的一个实现,它在构造函数中创建一个BlockingQueue,用它保存完成的结果。计算完成会调用FutureTask中的done方法,当提交一个任务后,首先把这个任务包装为一个QueueingFuture,他是Future的一个子类,然后复写done方法,将结果置入BlockingQueue中。take和poll方法委托给了BlockingQueue,它会在结果不可用时阻塞。

public class ExecutorCompletionService<V> implements CompletionService<V> {
    private final Executor executor;
    private final AbstractExecutorService aes;
    private final BlockingQueue<Future<V>> completionQueue;

   
    private class QueueingFuture extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task) {
            super(task, null);
            this.task = task;
        }
        protected void done() { completionQueue.add(task); }
        private final Future<V> task;
    }

    private RunnableFuture<V> newTaskFor(Callable<V> task) {
        if (aes == null)
            return new FutureTask<V>(task);
        else
            return aes.newTaskFor(task);
    }

    private RunnableFuture<V> newTaskFor(Runnable task, V result) {
        if (aes == null)
            return new FutureTask<V>(task, result);
        else
            return aes.newTaskFor(task, result);
    }

   
    public ExecutorCompletionService(Executor executor) {
        if (executor == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
        this.completionQueue = new LinkedBlockingQueue<Future<V>>();
    }

    
    public ExecutorCompletionService(Executor executor,
                                     BlockingQueue<Future<V>> completionQueue) {
        if (executor == null || completionQueue == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
        this.completionQueue = completionQueue;
    }

    public Future<V> submit(Callable<V> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task);
        executor.execute(new QueueingFuture(f));
        return f;
    }

    public Future<V> submit(Runnable task, V result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task, result);
        executor.execute(new QueueingFuture(f));
        return f;
    }

    public Future<V> take() throws InterruptedException {
        return completionQueue.take();
    }

    public Future<V> poll() {
        return completionQueue.poll();
    }

    public Future<V> poll(long timeout, TimeUnit unit)
            throws InterruptedException {
        return completionQueue.poll(timeout, unit);
    }

}

六:总结

围绕任务的执行来构造应用程序,可以简化开发,便于同步。Executor框架有助于你在任务的提交与任务的执行策略之间进行解耦,同时还支持很多不同类型的执行策略。

 

基于Python的SEC Edgar文件高效爬取:最新技术与实战指南 本文详细介绍了如何使用Python最新技术高效爬取SEC Edgar数据库中的金融文件。我们将探讨SEC Edgar系统的结构、合法爬取注意事项,并使用现代Python库如Requests-HTML、aiohttp和Playwright实现多种爬取方案。文章包含完整的代码示例、性能优化技巧以及数据处理方法,为金融数据分析师、研究人员和开发者提供全面指导。关键词:SEC Edgar、Python爬虫、金融数据、异步爬取、Playwright、XBRL解析。 阅读详情

相关推荐

BookSim2.0 教程

本文是yz关于BookSim2.0的个人学习经验与教程。 1.介绍 什么是BookSim2.0 BookSim2.0 是周期正确的互联网络模拟器(A Cycle-Accurate Interconnection Network Simulator),被设计用来伴随William James Dally 等人出版的的书籍 Principles and Practices of INterconnection on Networks使用。 此后其功能不断得到扩展。当前的主要版本 BookSim 2.0 支持广泛

更新一些计算机体系架构NoC,GEM5, AXI4,还有machine learning 的日常 5737

springboot多线程任务执行TaskExecutor的使用,以及使用@Async实现异步调用:自定义线程池

1.@Async实现异步调用 1.1pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 1.2.启动类 ...

迷茫,是因为才华配不上梦想... 4802

2025小红书跳转卡片实战指南:从零搭建独立后台与自动化发卡系统

本文提供了一份2025年小红书跳转卡片的实战搭建指南,详细解析了独立后台系统的核心架构与自动化发卡流程。文章从零开始,手把手教你使用Node.js、Vue.js等技术栈构建包含后台管理、用户制卡中心与前端交互的完整系统,旨在帮助运营者实现合规引流,并掌握数据自主权与自动化运营能力。

weixin_29234423的博客 518

Java并发编程:线程池的使用

Java并发编程:线程池的使用   在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:   如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。   那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?   在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池,首先我们从最核心的Thre

weixin_42383680的博客 125

贝叶斯校准在计算机模型不确定性分析中的实践指南

本文深入探讨了贝叶斯校准在计算机模型不确定性分析中的核心原理与实践方法。通过将模型从“黑箱”转变为“透明箱”,该方法系统量化了参数不确定性、模型结构缺陷及观测误差。文章重点阐释了如何利用高斯过程作为代理模型和模型不足函数,构建完整的贝叶斯校准框架,并提供了从数据准备、模型训练到MCMC推断及不确定性分析的实战指南,最终实现更可靠的预测与决策支持。

weixin_29088655的博客 292

从婴儿学步到工业级应用:VLA的‘隐式标定‘原理通俗图解

本文深入解析VLA(Vision-Language-Action)模型的'隐式标定'原理,通过类比婴儿学习机制,揭示其如何替代传统手眼标定技术。文章对比了工业机器人传统标定与VLA数据驱动方法的差异,并可视化神经网络注意力机制,展示其在空间关系编码上的突破性进展。最后探讨了VLA在工业应用中的优势与混合架构解决方案。

pz890123456的博客 324

JAVA 定时任务实现方式

定时任务

qq_41482600的博客 7519

SAP CO模块实战:成本中心会计分割结构配置全流程(OKES事务码详解)

本文详细解析了SAP CO模块中成本中心会计分割结构的配置全流程,重点讲解了OKES事务码的实战操作。内容涵盖分割结构的概念理解、配置前的次级成本要素与作业类型主数据准备,并通过步骤拆解演示了如何定义分割结构、规则与版本,以及如何将配置关联到成本中心并执行分割。最后提供了常见问题排查与优化建议,旨在帮助顾问及财务人员掌握成本精确分摊的关键配置,确保期末成本核算的准确性。

embedding5hiker的博客 805

并发编程之ThreadPoolTaskExecutor与ThreadPoolExecutor的区别

因此Spring的线程池在初始化对象时,也便初始化了JDK的线程池并赋值,从而操作Spring线程池处理多线程任务时,其实本质上还是JDK的线程池在执行。3、如果任务可以成功加入队列,那么我们仍然需要再次检查是否应该添加线程(因为自上次检查以来,已有的线程已死亡)或自进入此方法后线程池已关闭。2、获取线程池中的线程数,如果运行的线程少于corePoolSize数量,会尝试以给定命令作为第一个线程启动新线程(核心线程)的任务。3、判断如果JDK线程池对象不为空,则给线程池赋值核心线程数。

mingyonghu的博客 1111

ESP32开发环境避坑指南:用Thonny烧录MicroPython固件时常见的5个错误及解决方法

本文针对ESP32开发新手,详细解析了使用Thonny IDE烧录MicroPython固件时最常见的5个错误及其解决方案。内容涵盖驱动安装、端口识别、固件烧录失败、版本兼容性、连接不稳定以及文件系统操作异常等核心痛点,并提供硬件层面的深度排查指南,帮助开发者高效搭建稳定的ESP32开发环境。

weixin_29189003的博客 407

基于Simulink的永磁同步电机容错控制(缺相容错)

本文基于Simulink平台,详细介绍了永磁同步电机(PMSM)缺相容错控制的仿真实现方法。针对逆变器开路故障导致的缺相问题,提出"检测-重构-补偿"三阶段控制策略:采用电流阈值法快速检测故障(<5ms),重构αβ坐标系实现两相矢量控制,并通过谐波电流注入抑制转矩脉动(<8%)。仿真结果表明,该方法能有效维持故障后电机运行,转速波动<3%,满足电动汽车等关键应用的可靠性需求。文章包含完整的Simulink建模步骤、MATLAB函数代码及参数设置,为工程实践提供了可复现的

xiaoheshang_123的博客 146

Spring Boot(5) @Async异步线程池详解

一. Spring异步线程池的接口类 :TaskExecutor Spring异步线程池的接口类,其实质是java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。 2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方 3. ConcurrentTaskExecutor:Executor...

黄规速博客:学如逆水行舟,不进则退 9792

phpMyAdmin | mysqli::real_connect(): (HY000/2002): No such file or directory

第一次安装宝塔mysql服务是默认关闭的,需要手动打开,打开服务再次进入phpMyAdmin发现可以进入了。

吻等离子的博客 2872

Java并发编程新纪元:探索Executor线程池框架

在Java并发编程领域,线程的创建与管理至关重要。传统通过new Thread()的方式在性能优化、资源高效利用及系统扩展性方面存在明显局限。鉴于此,本文将介绍Executor线程池框架,这一相较于传统线程创建方式更为优越的并发编程工具。Executor线程池框架通过复用线程、智能管理资源及灵活配置,显著提升了系统性能、优化了资源利用率,并增强了系统的可扩展性。

Rye的博客 2649

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Java并发编程框架之Executor 框架

Java 并发编程框架是用于简化并发程序开发的一系列工具和库,它们提供了高层次的抽象来管理线程、同步机制和其他与并发相关的资源。随着Java语言的发展,这些框架不断进化,为开发者提供更加高效且易于使用的API。

物联网工程专业 - 软硬兼修 980

计算机控制技术及应用

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

java并发编程【二】Executor框架与线程池

线程池是池化技术的一种实现,在并发编程中起到了至关重要的的作用

weixin_44050211的博客 1952

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

java并发编程:Executor、Executors、ExecutorService

Executors    在Java 5之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好...

River的博客 8万+

《Java并发编程的艺术》读后笔记-Executor框架简介(第十章)

文章目录《Java并发编程的艺术》Executor框架简介(第十章)1.Executor框架的两级调度模型2.Executor框架的结构与成员2.1 Executor框架的结构2.2 Executor框架的成员一、ThreadPoolExecutor二、ScheduledThreadPoolExecutor三、Future接口四、Runnable接口和Callable接口 《Java并发编程的艺术》Executor框架简介(第十章) 1.Executor框架的两级调度模型 在HotSpot VM的线程模型中

小皮皮LL的博客 1174

Java并发编程学习10-任务执行与Executor框架

本篇重点讲解任务执行和Executor框架的基础知识

Huazie 1348

Java并发编程系列之十五:Executor框架

Java使用线程完成异步任务是很普遍的事,而线程的创建与销毁需要一定的开销,如果每个任务都需要创建一个线程将会消耗大量的计算资源,JDK 5之后把工作单元和执行机制区分开了,工作单元包括Runnable和Callable,而执行机制则由Executor框架提供。Executor框架为线程的启动、执行和关闭提供了便利,底层使用线程池实现。使用Executor框架管理线程的好处在于简化管理、提高效率,还

rhwayfun专栏 9669

Executor框架详解

你可以创建一个自定义的线程池,指定核心线程数、最大线程数、线程存活时间以及任务队列和拒绝策略。System . out . println("任务 " + r . toString() + " 被拒绝");i < 10;i ++) {try {System . out . println("任务 " + index + " 由 " + Thread . currentThread() . getName() + " 执行");

qq_34358193的博客 1321

Java 并发编程 Executor 框架

Excutor 框架 1. 两级调度模型 在 HotSpot VM 的线程模型中,Java 线程被一对一映射为本地操作系统线程。在上层,Java 多线程程序通常应用分解成若干个任务,然后使用用户级的调度器(Executor)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器。这种两级调度模型的示意图如图所示: 从图中可以看出,应用程序通过 Executor 框架控制上层调度,下层的调度则由操作系统内核控制 2. 框架结构 Ex..

低吟不作语的博客 344

java并发编程--Executor框架

对Executor框架简介 Executor框架实现了线程的提交和执行的解耦开发,易于扩展,采用线程中的生产者和消费者模式进行解耦开发,比如线程的提交相当于生产者,线程的执行相当于消费者. 1.Executor框架继承实现介绍: Executor的UML图: Executor:一个接口,只有一个接收runnable的构造方法: ExecutorService:比Executor...

moxiaolin的博客 458

Executor框架

转载请以链接形式标明出处: 本文出自:103style的博客 前言 在Java中,使用线程来异步执行任务。 Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源。 同时,为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。 Java的线程既是工作单元,也是执行机制。 从 JDK 5 开始,把 ...

103style 2312

Java并发编程-Executor框架

我们在并发编程创建线程时,常常是创建一些Runnable对象,然后创建对应的Thread对象执行它们,但是如果程序需要并发执行大量的任务时,需要为每个任务都创建一个Thread,进行管理,这将会影响程序的执行效率,并且创建线程过多将会使系统负载过重。Java 5之后通过了一套Executor框架能够解决这些问题,能够分解任务的创建和执行过程。该框架包括Executor,ExecutorService

记忆力不好的博客 5819

Java并发编程——Executor框架

今天看到《Java并发编程实战》第六章的时候,书中讲了利用Executor框架来实现多线程,平时Executor用得不多,今天顺带学习了一下,写出来总结总结。 引入Executor框架的原因 两种多线程执行任务的策略: 1.串行执行,即每次线程运行完才会运行下一个线程。这种方法是线程安全的,但是效率低,吞吐量小 2.为每个任务开辟一个线程,例如为每个请求服务器的连接建立一个线程,这种方法开...

qq_39479426的博客 366
上一篇: JAVA并发编程笔记(一)
下一篇: JAVA并发编程笔记(三)-应用线程池(ThreadPoolExecutor)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值