ArrayBlockingQueue 实现多线程安全 —————— 开开开山怪

深入浅出阻塞队列BlockingQueue及其典型实现ArrayBlockingQueue 本篇我们将一起对 AQS的另一类的应用场景的实现类——阻塞队列(实现生产者/消费者模型的经典方法) 做一次深入浅出的分析。从上面的类图结构和源码的注释分析来看,我总结如下:从上面总结的第一和第二点我们知道,BlockingQueue的接口方法包含四种类型的: 会抛异常的、返回特殊值的、会阻塞的和阻塞有超时的 ;但是按照操作结果大体可分为三种(官方区分的): 插入型的、删除型的、读取型的 。如下面的表格总结(其实也是源码的注释内容):正在上传…重新上传取消下面我们就此表格说明下( 我又多分了一个种类->读取删 阅读详情

主要内容

ArrayBlockingQueue.

这篇博客主要说说ArrayBlockingQueue这个阻塞队列的存储结构以及针对多线程的情况,这个阻塞队列是怎样实现多线程安全的,还有就是一些方法的区别。
其实对于多线程我也是慢慢体会到的,看了源码,看了别人的实现方式,才渐渐懂得怎样处理多线程安全的问题。
ArrayBlocking采用的是数组的存储方式。
希望你们也是如此。

当然在说的过程,采用源码来说是比较有权威的。

ArrayBlockingQueue

ArrayBlockingQueue是采用的是循环数组的形式表达队列,所以在该类中不存在扩容的情况,对于数组的长度来说,根据初始化的参数为标准,类中没有默认的数组长度。
其实对于这个类是怎样来实现多线程安全的呢,从这个类的成员就可以猜想到。

public class ArrayBlockingQueue<E> extends AbstractQueue<E>  implements BlockingQueue<E>, java.io.Serializable {
       final Object[] items;//存放元素的数组
       int takeIndex;//记录元素被取出元素的数组下标
       int putIndex;//记录元素被放入元素的数组下标
       int count;//记录元素的个数
       final ReentrantLock lock;//锁,同样也是保证多线程安全的一个重要因素
       private final Condition notEmpty;
       //notEmpty是当前lock的阻塞队列
       //作用就是采用内部的一个Condition队列来存储想通过put进行添加元素,但由于数组已满而被阻塞的线程。
       private final Condition notFull;
       //notFull是当前lock的另一个阻塞队列
       //作用就是采用内部的Condition队列来存储想通过take进行取出元素,但由于数组为空而被阻塞的线程。
       transient Itrs itrs = null;
}  

刚才说,可以通过成员来推测出这个类采用的什么手段来实现多线程。其实我是看完之后才进行总结的,不是我一开始看的时候就知道。

那我们一步一步来看,
这个类给我们提供了很多添加和取出的方法,而多线程安全的处理当然是在方法上进行处理的。
那为什么说通过成员就可以看出来实现手段呢,
int count 这个成员是用来记录数组中当前拥有的元素的个数,那么通过添加和取出都要对这个变量进行操作,而这个成员并没有采用volitile进行修饰。但是该类采用了一个ReentrantLock lock,那我们就可以得出结论,这个类在所有的方法上都采用lock进行多线程安全的保证。所以对于count来说可以不添加任何有关多线程安全的修饰。

为什么这么说呢,那首先就要对ReentrantLock 有一定的认识,如果不了解请看博客ReentrantLock.相信你就会理解。
因为我们知道一个ReentrantLock 对象对应一个AQS,所以对于ArrayBlockingQueue来说,如果你对一个ArrayBlockingQueue对象进行操作(加入或者取出),那当然对于这个对象来说也只有一个ReentrantLock 对象作为其ArrayBlockingQueue类的成员。
同样是因为在ArrayBlockingQueue类中的加入和取出的方法中都要对其lock进行获取,而lock对于一个ArrayBlockingQueue对象来说只有一个,那么在多线程进行调用加入或者取出的方法的时候,自然会排队进行,因为对于多线程来说,他们认识的是同一把锁lock,当有线程在执行加入或者取出的方法的时候,自然会持有这把锁,那么对于其他线程来说就只能加入到lock锁的等待队列中,等一个线程执行完,释放锁唤醒等待队列中的等待线程的时候,那些等待线程才会去挨个执行他们的操作。

所以对于ArrayBlockingQueue的成员count 没有采用任何的修饰,只是一个普通的变量,因为对于每个线程对ArrayBlockingQueue 对象所执行的操作都是顺序的,所以count具有准确性。

那我们来看ArrayBlockingQueue这个类中给我们提供的操作方法。
不是白看的,你们找这些个方法的一个共同点。你们找完之后,我会慢慢说这些个方法的具体情况。

poll();
 public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();//这里的lock就是ArrayBlockingQueue类中唯一的锁lock。
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
 }
offer(E e)
public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }
put(E e)
 public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
take()
 public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

找出来了吗?
通过上边的方法,我们也能清楚的看到,每个方法开头都采用了lock.lock,方法的结尾都采用了lock.unlock。
所以可以说对于ArrayBlockingQueue来说,实现多线程的手段比较粗鲁,就整了一个ReentrantLock 对象作为成员,就可以让多线程在方法的执行上做到同步了,同样也就实现类多线程安全。
所以ArrayBlockingQueue类中还有其他成员
final Object[] items;
int takeIndex;
int putIndex;
例如这三个成员,也都没有任何的关于线程安全的修饰,因为一个lock就做到了。

下面我们来说说offer()和put()的区别,take() 和poll() 的区别

put() 和 offer()

说到offer你们可以回看上面的方法,对于offer来说,当一个线程调用offer的时候,如果此时数组已经满员了,那么offer会直接返回false。表示不能够添加
而对于put()来说,同样也是添加,但是就不一样了,当一个线程调用put()的时候,如果此时数组已经满员了,那么你回看上边的方法,你会看到 notFull.await();这样的代码,此时对于调用put方法的这个线程来说,会进行阻塞,而不是像offer一样直接返回false。这个nofull成员我在一开始的时候已经说过了。
其实在这个notfull所带领的阻塞队列中的线程表明都是等待添加的线程,如果你看过我写的博客Condition.你就会知道Condition阻塞队列的原理。然后就等待被唤醒了。

take() 和 poll()。
poll方法可以回看上边的源码,表示取出一个元素,当数组为空时,则直接返回null。
而take方法,倒是和put方法有的一拼,当数组为空时,会执行notEmpty.await();这个代码,表明将调用take方法的线程进行阻塞。但对于区操作,是将操作的这个线程阻塞起来,加入到notEmpty带领的这个阻塞队列中。然后就等待被唤醒了。

其实不管是notEmpty和是notFull,这两个Condition对象都是由lock这一个对象生成的,说明不管是在两个阻塞队列中任何一个阻塞线程,在被唤醒之后都是要进入到lock的等待队列中的。因为不管在什么时候,只有可能是一个线程持有锁。

既然说到了阻塞线程需要被唤醒,何时被唤醒?

对于put来说,表明数组中没有位置,所以无法put,只能阻塞,当一旦有poll操作的时候,poll方法会调用一个dequeue方法这个方法就是将数组中最老的元素进行取出,关键是在最后有一句 notFull.signal();
采用这句进行唤醒在notFull阻塞队列中的线程,被唤醒的线程加入到lock的等待队列中,等待机会执行。

 private E dequeue() {
        final Object[] items = this.items;
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

对于take来说,数组为空时,无法获取,进行阻塞,当一旦有offer操作的时候,offer操作会调用enqueue()方法,进行元素的添加,在最后进行notEmpty.signal();唤醒等待取操作的线程,被唤醒的线程加入到lock的等待队列中,等待机会执行。

 private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

总结:
对于ArrayBlockingQueue类来说,采用实现多线程安全的手法还是比较简单,但同时也是我们可能最常用的一种手段,最主要的还是ArrayBlockingQueue应用了Reentrantlock这个类来进行实现的,还有Condition与Reentrantlock的结合。ArrayBlockingQueue中对元素的添加和删除的本质对循环数组的操作我们都很熟悉,最主要的还是能够理解Reentrantlock和Condition这两个类的原理,这样再回头自己看这个应用这两个类的阻塞队列的实现就会变得简单很多。

【项目实战】并发编程之Java集合框架中的一个线程安全的队列实现 ——BlockingQueue入门介绍 BlockingQueue是一个Java接口,表示一个线程安全的队列,当队列为空或已满时会阻塞。它是管理并发访问共享资源的有用工具。 阅读详情

相关推荐

基于OFDM的实时图像传输系统MATLAB仿真:MPSK与MQAM调制性能对比与误码率分析

本文详细介绍了基于OFDM的实时图像传输系统在MATLAB中的仿真实现,重点对比了MPSK与MQAM调制方式的性能差异与误码率分析。通过搭建OFDM系统模型,探讨了QPSK和16QAM在不同信道条件下的表现,包括频谱效率、抗噪能力和误码率等关键指标。文章还提供了MATLAB代码示例和实用技巧,帮助读者快速掌握OFDM图像传输系统的核心算法与优化方法。

weixin_29064009的博客 355

ArrayBlockingQueue

概述 ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列。 线程安全是指,ArrayBlockingQueue内部通过互斥锁保护竞争资源,实现了多线程对竞争资源的互斥访问。 有界是指,ArrayBlockingQueue对应的数组是有界限的。 阻塞队列是指,多线程访问竞争资源时,当竞争资源已被某线程获取时,其他要获取该资源的线程需要阻塞等待;而且ArrayBlockingQueue是按FIFO(先进先出)原则对元素进行排序,元素都是从尾部插入到队列,从头部开始返回。 ArrayB

weixin_38910645的博客 947

设计空间探索:乘法器设计的面积、延时、功耗优化

4-2压缩器在减少延迟方面表现出色,适用于需要快速压缩的场景。6-2压缩器在资源占用方面具有优势,能够减少压缩树的层次数量和逻辑门数量。优化功耗:通过合理组合不同类型的压缩器,可以在延迟、资源占用和功耗之间找到最佳平衡点,从而实现优化的设计目标。###参考资料。

SiArch的博客 1879

java 常用并发队列- ArrayBlockingQueue

是一个线程安全的、基于数组的有界阻塞队列。它在初始化时需要指定队列的容量,一旦创建,容量就不能改变。它的实现采用了锁和条件变量来控制并发访问,并提供了线程间的安全通信机制。是 Java 并发编程中一种常用的有界阻塞队列,适合生产者-消费者模型及需要在多个线程之间安全地共享数据的场景。通过显式锁和条件变量,提供了简单而有效的线程间通信机制。尽管它有容量限制和可能的锁竞争问题,但在需要固定容量控制和阻塞操作的场景中,是一个非常实用的选择。是 Java 中常用的并发队列之一,属于阻塞队列的范畴。它是一个。

Flying_Fish_roe的博客 1651

从零开始学嵌入式之STM32——1.什么是STM32

它是ST公司推出的一系列单片机的统称。ST----指的是ST公司----意法半导体。意法半导体,成立于1987年,是由意大利的SGS微电子公司和法国的Thomson半导体公司合并而成。ARM目前主要是ARM架构处理器及外围组件的电路设计方案,知识产权授权的形式收取费用,其他公司根据ARM的方案设计出CPU产品。,它以应用为核心,软硬件可裁剪,旨在实现对设备的控制、监测或辅助功能,区别于通用计算机(如 PC、服务器)的 “一机多用” 属性。在嵌入式领域,目前主流的是cortex-M3内核推出的芯片。

qshdyy的博客 1854

Unity 热更新技术 | (二) AssetBundle - 完整系列教程学习

开始学习热更新方面系列知识,就从这一篇开始吧! 本系列教程 会从热更新基本概念出发,详细介绍热更新相关的全套知识点。 如热更新技术基本原理、热更新主流方案、AssetBundle系列教程、Lua语言编程、Lua与Unity交互教程、Xlua框架使用、热更新实战教程等方面。 热更新作为一个开发者必备技能,如果想学习的话,可以从现在开始了哦~ 接下来从 AssetBundle - 完整系列教程学习 开始学习吧!

努力前行,总会成为自己心中的那道光 1万+

线程安全的队列-ArrayBlockingQueue源码分析

一,ArrayBlockingQueue源码分析ArrayBlockingQueue是队列的一种,队列的特点嘛,先出先出,然而这种队列是一种线程安全阻塞式的队列,为什么是阻塞式队列?我想...

qq_35006660的博客 1186

Android 车联网——CarAudioService介绍(六)

CarAudioService 是 Android Automotive OS 中的一个重要组件,用于管理车辆中的音频功能。它是 Android Auto 的一部分,负责处理车辆音频的播放、控制和路由。CarAudioService 使用标准的 Android 组件架构,基于 Binder 通信机制,提供了一组 API,用于管理车辆音频。它包括以下主要功能:音频路由管理:CarAudioService 可以让开发者控制音频的路由,即将音频发送到不同的输出设备,例如车辆的扬声器、耳机等。

小旭的专栏 3201

多线程基础(六、二)BlockingQueue用法详解之ArrayBlockingQueue

BlockingQueue用法详解之ArrayBlockingQueue

m0_47743175的博客 4306

4、LTE无人机认证:保障通信安全的新方案

本文针对LTE网络中无人机通信存在的安全风险,提出了一种基于可信第三方GCS的新型认证协议。该协议通过非对称加密和哈希函数保护IMSI等敏感信息,防止身份泄露、中间人攻击、拒绝服务攻击和位置追踪等威胁。利用Scyther工具对协议进行形式化分析,验证了其在机密性、一致性和活性等方面的安全属性,证明该方案能有效提升无人机在LTE网络中的通信安全性与隐私保护能力。

pink的博客 61

Python 爬虫实战:知网论文数据爬取(题录 / 摘要 / 引用提取 + Excel 结构化)

在学术研究中,获取准确且全面的文献数据是至关重要的。知网(CNKI)作为国内最大的学术文献数据库,拥有海量的论文资源。通过 Python 爬虫技术,我们可以高效地从知网爬取论文数据,包括题录、摘要和引用信息,并将这些数据结构化存储到 Excel 文件中,方便后续的学术分析和研究。

yansideyucsdn的博客 1749

java并发编程:ArrayBlockingQueue详解

ArrayBlockingQueue 顾名思义是基于数组的阻塞队列。数组是要指定长度的,所以使用 ArrayBlockingQueue 时必须指定长度,也就是它是一个有界队列。它实现了 BlockingQueue 接口,有着队列、集合以及阻塞队列的所有方法。ArrayBlockingQueue 是线程安全的,内部使用 ReentrantLock 来保证。ArrayBlockingQueue 支持对生产者线程和消费者线程进行公平的调度。

初念初恋的博客 2540

提升JVM性能:CMS垃圾回收器的优化分析与案例研究

本文介绍了CMS(Concurrent Mark-Sweep)垃圾回收器的工作原理、优缺点及常见问题,并通过具体案例分析了其优化策略。重点探讨了CMS的各个阶段,包括标记、并发清理和重标记,分析了可能导致性能瓶颈的因素如停顿时间过长和内存碎片化。文章还分享了调整垃圾回收参数、结合其他垃圾回收器以及内存优化等方法,帮助提高CMS的回收效率和性能。最后,文章展望了随着JVM发展,结合新一代垃圾回收器优化的趋势,助力Java应用在高并发环境下的性能提升。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 175万+

java-ArrayBlockingQueue详解

是一个基于数组实现的阻塞队列,它继承自并实现了接口。这个队列在创建时需要指定一个固定的大小,之后这个大小就不能再改变了。当队列满时,如果再有新的元素试图加入队列,那么这个操作会被阻塞;同样地,如果队列为空,那么从队列中取元素的操作也会被阻塞。这种特性使得非常适合作为生产者-消费者模式中的缓冲区。是Java并发编程中一个非常实用的工具类。它提供了线程安全的阻塞队列实现,支持生产者-消费者模式,并允许通过队列的大小来控制系统的流量。在使用。

大乐乐 2251

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Java阻塞队列:ArrayBlockingQueue

`ArrayBlockingQueue`是Java中的一个阻塞队列(Blocking Queue)实现,它是线程安全的,并且基于数组实现。`ArrayBlockingQueue`常用于生产者-消费者模型,在这种模型中,生产者线程负责将元素放入队列,而消费者线程负责从队列中取出元素。

常备不懈 704

VC中sqlite的使用

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

Java多线程系列--“JUC集合”07之 ArrayBlockingQueue

概要 本章对Java.util.concurrent包中的ArrayBlockingQueue类进行详细的介绍。内容包括:ArrayBlockingQueue介绍ArrayBlockingQueue原理和数据结构ArrayBlockingQueue函数列表ArrayBlockingQueue源码分析(JDK1.7.0_40版本)ArrayBlockingQueue示例 转载请注...

kk的博客 606

精雕软件(JDpaint) 5.5 绿色版

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

Java进阶篇--并发容器之ArrayBlockingQueue与LinkedBlockingQueue

它提供了put、take等方法来实现元素的添加和移除,并且在队列为空或已满时能够自动阻塞和唤醒相应的线程,以保证生产者和消费者之间的同步和协作。总之,ArrayBlockingQueue 是一种高效、线程安全的数据结构,适用于多线程环境下的生产者-消费者问题,其中生产者线程负责将元素插入队尾,消费者线程负责从队首取出元素进行处理。如果队列不为空,则获取并移除数据,并通知生产者线程。在这个例子中,我们使用了多个生产者和消费者线程,它们同时操作LinkedBlockingQueue,展示了队列的并发性质。

m0_74293254的博客 2434

深入解析Java多线程下 ArrayBlockingQueue

文章目录一.ArrayBlockingQueue 介绍 一.ArrayBlockingQueue 介绍 ArrayBlockingQueue是一个阻塞式的队列,继承自AbstractBlockingQueue,实现了BlockingQueue接口。 底层以数组的形式保存数据(Object [])。常用的操作包括 add,offer,put,remove,poll,take,peek。 public...

杨海吉 512

阻塞队列之四:ArrayBlockingQueue

一、ArrayBlockingQueue简介 一个由循环数组支持的有界阻塞队列。它的本质是一个基于数组的BlockingQueue的实现。 它的容纳大小是固定的。此队列按 FIFO(先进先出)原则对元素进行排序。 队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。 新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。 这是一个典型的“有界缓存区”,固...

weixin_33840661的博客 164

Java多线程——安全线程问题(ConcerrentHashMap、CopyOnWriteArrayList 、CopyOnWriteArraySet、ArrayBlockingQueue)

介绍CopyOnWriteArrayList CopyOnWriteArraySet 基础是CopyOnWriteArrayList,线程安全的Set,不允许重复 ConcurrentLinkQueue 无界的队列,然后是线程安全的Queue ArrayBlockingQueue 是一种线程安全的阻塞的Queue,(生产者、消费者问题) ConcerrentHashMap 一种线程安全的HashMap

m0_57532232的博客 537

JAVA多线程之——ArrayBlockingQueue

ArrayBlockingQueueArrayBlockingQueue队列是BlockingQueue的一个子类.block是阻塞的意思。它是一个阻塞队列,array说明它的底层是基于数组实现。数组是有大小的,所以它是一个有(边)界的队列。综上,ArrayBlockingQueue是一个有界的阻塞队列。 BlockingQueue BlockingQueue接口有三个删除方法和三个添加方法

永不止步 740

第十章 Java多线程--阻塞队列--ArrayBlockingQueue

ArrayBlockingQueue 是基于数组的有界阻塞队列。此集合类保证它的元素按照 FIFO(先进先出)原则进行排序。当构造 ArrayBlockingQueue 实例时,必须指定其容量大小,并且一旦创建就不能更改。

Lin_Miao_09的博客 1210

ArrayBlockingQueue 和 LinkedBlockingQueue

顾名思义:基于数组的阻塞队列。数组是要指定长度的,所以使用时必须指定长度,也就是它是一个有界阻塞队列。(有界是指他的容量大小是固定的,不能扩充容量,在初始化时就必须确定队列大小。它实现了接口,有着队列、集合以及阻塞队列的所有方法。它内部使用来保证线程安全。支持对生产者线程和消费者线程进行公平的调度,默认情况下是不保证公平性的。公平性通常会降低吞吐量,但是减少了可变性和避免了线程饥饿问题。是一个阻塞队列,内部由来实现线程安全,由Condition的await和signal来实现等待唤醒的功能。

qq_24907431的博客 2376

JAVA多线程(二十五)Java多线程之ArrayBlockingQueue容器

1.JAVA多线程(二十五)Java多线程之ArrayBlockingQueue容器 1.1 什么是BlockingQueue   阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中,其原因是 BlockingQueue 提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。 Blocki...

yuan_xw的专栏 672

多线程(十八、阻塞队列-ArrayBlockingQueue)

ArrayBlockingQueue简介 1、队列的容量一旦在构造时指定,后续不能改变; 2、插入元素时,在队尾进行;删除元素时,在队首进行; 3、队列满时,插入元素会阻塞线程;队列空时,删除元素也会阻塞线程; 4、支持公平/非公平策略,默认为非公平策略。 ArrayBlockingQueue构造 核心构造方法: /** * 指定队列初始容量和公平/非公平策略的构造器. */ public A...

ceoaxv5008的博客 272

ArrayBlockingQueue和LinkedBlockingQueue解析

1. BlockingQueue接口简介 java.util.concurrent 包里的BlockingQueue是一个接口,继承Queue接口,Queue接口继承Collection。 BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下: 抛出异常 特殊值...

傅红雪的专栏 1700
上一篇: Reentrantlock和其搭档Condition —————— 开开开山怪
下一篇: LinkedBlockingQueue多线程安全的保障 —————— 开开开山怪
开开开山怪
博客等级 码龄8年 41粉丝 30原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值