JAVA 同步实现原理

【Java并发】synchronized 回答锁消除是 JVM 的优化手段:JIT 编译器检测到某些同步代码块的锁对象不存在共享竞争(如局部对象、逃逸分析判定为线程私有),会自动移除锁,减少性能开销。回答JVM 设定偏向锁批量撤销阈值为 40 次,是「性能权衡」的结果,回答synchronized 是 Java 并发的核心考点,面试答题需遵循「先定义→再原理→再场景→最后优化基础层:3 种用法+锁对象,避免基础用法错误;原理层:Mark Word+Monitor+锁升级,理解底层实现;优化层。 阅读详情

Synchronized的基本使用

Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:

  1. 确保线程互斥的访问同步代码
  2. 保证共享变量的修改能够及时可见
  3. 有效解决重排序问题。

从语法上讲,Synchronized总共有三种用法:

  1. 修饰普通方法
  2. 修饰静态方法
  3. 修饰代码块

接下来我就通过几个例子程序来说明一下这三种使用方式(为了便于比较,三段代码除了Synchronized的使用方式不同以外,其他基本保持一致)。

  • 没有同步的情况

package com.paddx.test.concurrent;

 

public class SynchronizedTest {

    public void method1(){

        System.out.println("Method 1 start");

        try {

            System.out.println("Method 1 execute");

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 1 end");

    }

 

    public void method2(){

        System.out.println("Method 2 start");

        try {

            System.out.println("Method 2 execute");

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 2 end");

    }

 

    public static void main(String[] args) {

        final SynchronizedTest test = new SynchronizedTest();

 

        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method1();

            }

        }).start();

 

        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method2();

            }

        }).start();

    }

}

执行结果如下,线程1和线程2同时进入执行状态,线程2执行速度比线程1快,所以线程2先执行完成,这个过程中线程1和线程2是同时执行的。

Method 1 start

Method 1 execute

Method 2 start

Method 2 execute

Method 2 end

Method 1 end

  • 对普通方法同步

package com.paddx.test.concurrent;

 

public class SynchronizedTest {

    public synchronized void method1(){

        System.out.println("Method 1 start");

        try {

            System.out.println("Method 1 execute");

            Thread.sleep(3000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 1 end");

    }

 

    public synchronized void method2(){

        System.out.println("Method 2 start");

        try {

            System.out.println("Method 2 execute");

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 2 end");

    }

 

    public static void main(String[] args) {

        final SynchronizedTest test = new SynchronizedTest();

 

        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method1();

            }

        }).start();

 

        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method2();

            }

        }).start();

    }

}

执行结果如下,跟代码段一比较,可以很明显的看出,线程2需要等待线程1的method1执行完成才能开始执行method2方法。

Method 1 start

Method 1 execute

Method 1 end

Method 2 start

Method 2 execute

Method 2 end

  • 静态方法(类)同步

package com.paddx.test.concurrent;

 

 public class SynchronizedTest {

     public static synchronized void method1(){

         System.out.println("Method 1 start");

         try {

             System.out.println("Method 1 execute");

             Thread.sleep(3000);

         } catch (InterruptedException e) {

             e.printStackTrace();

         }

         System.out.println("Method 1 end");

     }

 

     public static synchronized void method2(){

         System.out.println("Method 2 start");

         try {

             System.out.println("Method 2 execute");

             Thread.sleep(1000);

         } catch (InterruptedException e) {

             e.printStackTrace();

         }

         System.out.println("Method 2 end");

     }

 

     public static void main(String[] args) {

         final SynchronizedTest test = new SynchronizedTest();

         final SynchronizedTest test2 = new SynchronizedTest();

 

         new Thread(new Runnable() {

             @Override

             public void run() {

                 test.method1();

             }

         }).start();

 

         new Thread(new Runnable() {

             @Override

             public void run() {

                 test2.method2();

             }

         }).start();

     }

 }

执行结果如下,对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。

Method 1 start

Method 1 execute

Method 1 end

Method 2 start

Method 2 execute

Method 2 end

  • 代码块同步

package com.paddx.test.concurrent;

 

public class SynchronizedTest {

    public void method1(){

        System.out.println("Method 1 start");

        try {

            synchronized (this) {

                System.out.println("Method 1 execute");

                Thread.sleep(3000);

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 1 end");

    }

 

    public void method2(){

        System.out.println("Method 2 start");

        try {

            synchronized (this) {

                System.out.println("Method 2 execute");

                Thread.sleep(1000);

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Method 2 end");

    }

 

    public static void main(String[] args) {

        final SynchronizedTest test = new SynchronizedTest();

 

        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method1();

            }

        }).start();

 

        new Thread(new Runnable() {

            @Override

            public void run() {

                test.method2();

            }

        }).start();

    }

}

执行结果如下,虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。

Method 1 start

Method 1 execute

Method 2 start

Method 1 end

Method 2 execute

Method 2 end

Synchronized 原理

先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的

package com.paddx.test.concurrent;

 

public class SynchronizedDemo {

    public void method() {

        synchronized (this) {

            System.out.println("Method 1 start");

        }

    }

}

反编译结果

关于这两条指令的作用,我们直接参考JVM规范中描述: ` Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. • If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. • If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership. `

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

  1. 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
  2. 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
  3. 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

monitorexit:  ` The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.`

  1. 执行monitorexit的线程必须是objectref所对应的monitor的所有者。
  2. 指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

通过这两段描述,我们应该能很清楚的看出 Synchronized的实现原理

Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

我们再来看一下同步方法的反编译结果:

package com.paddx.test.concurrent;

 

public class SynchronizedMethod {

    public synchronized void method() {

        System.out.println("Hello World!");

    }

}

反编译结果

从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

运行结果解释

有了对Synchronized原理的认识,再来看上面的程序就可以迎刃而解了。

  1. 代码段2结果: 虽然method1和method2是不同的方法,但是这两个方法都进行了同步,并且是通过同一个对象去调用的,所以调用之前都需要先去竞争同一个对象上的锁(monitor),也就只能互斥的获取到锁,因此,method1和method2只能顺序的执行。
  2. 代码段3结果: 虽然test和test2属于不同对象,但是test和test2属于同一个类的不同实例,由于method1和method2都属于静态同步方法,所以调用的时候需要获取同一个类上monitor(每个类只对应一个class对象),所以也只能顺序的执行。
  3. 代码段4结果: 对于代码块的同步实质上需要获取Synchronized关键字后面括号中对象的monitor,由于这段代码中括号的内容都是this,而method1和method2又是通过同一的对象去调用的,所以进入同步块之前需要去竞争同一个对象上的锁,因此只能顺序执行同步块。

总结

Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到内核态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。

因此,这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“偏向锁”和“轻量级锁”。无锁 --> 偏向锁 --> 轻量级 --> 重量级

ArcGIS基于DEM提取微流域单元(附练习数据下载) 流域作为一个相对独立的地理单元,与行政区的划分有着本质上的区别。流域的划分更注重自然因素,流域作为一个综合的自然整体,在其内部形成了一个天然的物质与能量流动的场所,并通过水流的作用,与周围的区域进行物质和能量的交换。流域的精确划分作为一个研究的基础工作,那么接下来给大家说一下利用DEM提取小流域的案例教程——以贵州省遵义市正安县为例。 1 数据获取 地理空间数据云(http://www.gscloud.cn/),数据分辨率为30m。也可直接点击这篇直接下载→全国30m90mDEM下载。 若想要获得更高精度. 阅读详情

相关推荐

300m半径无人机单兵侦测设备技术详解

300m半径无人机单兵侦测设备凭借其卓越的侦测性能、高效的识别能力、便捷的告警方式以及广泛的应用场景,成为现代军事、安保及应急救援领域的重要装备。300m半径无人机单兵侦测设备,融合了最新的无人机技术、高精度传感技术与实时数据处理技术,为单兵作战、巡逻及侦察任务提供了前所未有的视野与效率。该设备通过无人机搭载的高性能侦测模块,能够在复杂环境中实现300米范围内的精准侦测,大幅提升单兵的战场感知能力和决策效率。设备为被动式探测设备,确保安静安全运行,不暴露目标 设备耗电功率小,可长时间在野外使用。

专注多旋翼、复合翼无人机技术研究,从事低空经济行业。 1120

JAVA 同步实现原理同步实现原理

JAVA 同步实现原理下面的文章是转载的,虽然看了半天没有看懂,但是喜欢最后的总结所说的:这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用转载自:http://www.importnew.com/29031.htmlSynchronized的基本使用Synchronized是Java中解决并...

zhong12270107的博客 2896

从3dB定向耦合器到双圆极化天线:在HFSS里玩转相位差90°的馈电艺术

本文详细介绍了在HFSS中设计3dB定向耦合器与双圆极化天线的关键技术,重点解析了90°相位差生成原理及馈电网络设计。通过HFSS仿真工具,实现从定向耦合器到完整天线系统的集成,优化阵列天线性能,适用于卫星通信和雷达系统。

weixin_33717117的博客 556

深入解析 Java 中 synchronized 的实现原理与机制

的底层实现融合了对象头 Mark WordMonitor 机制CAS 操作和锁升级策略,是 JVM 层面的核心同步机制。理解其原理需要结合 JVM 源码和操作系统知识,在实际开发中需根据场景选择合适的锁粒度和类型。

weixin_43718423的博客 998

自动驾驶IDM与MOBIL模型

IDM模型,自动驾驶

不想学好数学的汽车工程师不是一个好的程序员 1万+

【PyTorch项目实战】图像分类与识别:手写数字识别(MNIST)、普适物体识别(CIFAR-10)

主要介绍了两个经典的实战项目:(1)基于 MNIST 数据集的手写数字识别(2)基于 CIFAR-10 数据集进行图像分类

📷 图像处理践行者 | 传统算法 · 深度学习 · 多模态 | 分享实战经验与技术洞见,探索从像素到智能的无限可能。 4401

Java多线程——synchronized使用详解

一 前言 Java多线程是面试必考的知识点,哈哈,说的有点太绝对了,题外话不说那么多了,今天我们就来了解一下Java多线程中的synchronized。 synchronized相信大家都看过或者用过,synchronized是Java中的关键字,synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性,Java中每一个...

zhangqiluGrubby的博客 8万+

apache 403错误 Forbidden解决方法

当网页打开提示以下错误时,即出现了403错误。 Forbidden You don't have permission to access / on this server. 403 Forbidden 资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。通常情况可能(但不限于此)的原因有: 原因1:apache配置文件中没有对站点目录的权限许可,这通...

lmseo5hy的博客 1499

Java 多线程(六) synchronized关键字详解

Java 多线程(六) synchronized关键字详解   多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题。   同步机制可以使用synchronized关键字实现。   当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。   当synchronized方法执行完或发生异常时,会自动释放锁。...

weixin_34247032的博客 256

U盘插入电脑时发出声音但不显示——编程解决方案

以上是一些常见的编程解决方案,帮助您解决U盘插入电脑时发出声音但不显示的问题。请注意,这些解决方案可能因不同的情况而略有不同,因此请根据您的具体情况进行调整和尝试。当您将U盘插入电脑时,如果您听到了连接声音,但是U盘却没有在文件资源管理器或桌面上显示出来,这可能是由于一些常见问题导致的。U盘的文件系统格式可能与您的计算机不兼容,导致无法正确识别。如果输出显示U盘的文件系统格式与您的计算机不兼容,您可以尝试重新格式化U盘或使用其他兼容的文件系统格式。其中,X是U盘的磁盘编号,Y是您想要分配给U盘的盘符。

ByteEchoX的博客 2463

11、基于STM32的智能药盒

当今时代是一个新技术层出不穷的时代,在电子领域尤其是自动化智能控制领域,传统的分立元件或数字逻辑电路构成的控制系统,正以前所未有的速度被单片机智能控制系统所取代。嵌入式具有体积小、功能强、成本低、应用面广等优点,可以说,智能控制与自动控制的核心就是单片机。

行走的皮卡丘 6103

java sync 实现原理_JAVA 同步实现原理

原标题:JAVA 同步实现原理Synchronized的基本使用Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:确保线程互斥的访问同步代码保证共享变量的修改能够及时可见有效解决重排序问题。从语法上讲,Synchronized总共有三种用法:修饰普通方法修饰静态方法修饰代码块接下来我就通过几个例子程序来说明一下这三种...

weixin_33364134的博客 3751

Nvidia Driveos SDK/PDK介绍

NVIDIA DRIVEOS SDK/PDK介绍

u011803281的博客 2176

Java线程同步详解:原理、实现与最佳实践

Java线程同步详解:原理、实现与最佳实践

嗨,欢迎来到我的 优快云 博客小天地!一名深耕多年的技术发烧友。在这里,我将把日常工作中积累的宝贵经验,从复杂架构设计的精妙之处,到代码优化的实战技巧,毫无保留地分享给大家。 9615

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

java同步锁工作原理_Java内置锁synchronized的实现原理

简述Java中每个对象都可以用来实现一个同步的锁,这些锁被称为内置锁(Intrinsic Lock)或监视器锁(Monitor Lock)。具体表现形式如下:1、普通同步方法,锁的是当前实例对象2、静态同步方法,锁的是当前Class对象3、对于同步代码块,锁的是Synchronized括号中的代码块线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时自动释放锁,无论是通过正常路径退出,还是...

weixin_42528780的博客 324

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

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

java怎么实现线程同步的_Java实现线程同步方法及原理详解

一、概述无论是什么语言,在多线程编程中,常常会遇到多个线同时操作程某个变量(读/写),如果读/写不同步,则会造成不符合预期的结果。例如:线程A和线程B并发运行,都操作变量X,若线程A对变量X进行赋上一个新值,线程B仍然使用变量X之前的值,很明显线程B使用的X不是我们想要的值了。Java提供了三种机制,解决上述问题,实现线程同步:同步代码块synchronized(锁对象){// 这里添加受保护的数...

weixin_30058099的博客 154

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

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

java 同步块原理_JAVA 同步实现原理

Synchronized的基本使用Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:确保线程互斥的访问同步代码保证共享变量的修改能够及时可见有效解决重排序问题。从语法上讲,Synchronized总共有三种用法:修饰普通方法修饰静态方法修饰代码块接下来我就通过几个例子程序来说明一下这三种使用方式(为了便于比较,三段代...

weixin_33550394的博客 117

java同步阻塞队列之ArrayBlockingQueue实现原理

在java中我们可以用同步阻塞队列实现生产者-消费者模型。 ArrayBlockingQueue提供了阻塞队列功能,底层数据结构是基于数组,提供如下几个关键方法: public boolean add(E e) 向队列中添加元素底层调用的是offer方法,如果添加成功返回true,否则抛出IllegalStateException异常 public boolean offer(E e),向队列中添加元素,如果队列 已经满了,返回false,否则添加元素 public void put(E e) throw

LeoHan 1011

java同步锁工作原理_java中同步锁的原理和实现

接口 Lock实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition 对象。锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如ReadWriteLock 的...

weixin_30915399的博客 379

Java 中队列同步器 AQS(AbstractQueuedSynchronizer)实现原理

前言 在 Java 中通过 锁 来控制多个线程对共享资源的访问,使用 Java 编程语言开发的朋友都知道,可以通过 synchronized 关键字来实现锁的功能,它可以隐式的获取锁,也就是说我们使用该关键字并不需要去关心锁的获取和释放过程,但是在提供方便的同时也意味着其灵活性的下降。例如,有这样的一个场景,先获取锁 A,然后再获取锁 B,当锁 B 获取到之后,释放锁 A 同时获取锁 C,当获取锁 C 后,再释放锁 B 同时获取锁 D,依次类推,像这种比较复杂的场景,使用 synchronized 关键字就

Meldoy_mgh的博客 559

java同步锁工作原理_深入理解java:2.2. 同步锁Synchronized及其实现原理

同步的基本思想为了保证共享数据在同一时刻只被一个线程使用,我们有一种很简单的实现思想,就是在共享数据里保存一个锁,当没有线程访问时,锁是空的。当有第一个线程访问时,就在锁里保存这个线程的标识并允许这个线程访问共享数据。在当前线程释放共享数据之前,如果再有其他线程想要访问共享数据,就要等待锁释放。在共享数据里保存一个锁在锁里保存这个线程的标识其他线程访问已加锁共享数据要等待锁释放Jvm同步...

weixin_35193131的博客 151

「Java同步原理与底层实现解析」

每一个锁关联一个线程持有者和一个计数器、当计数器为0时表示该锁没有被任何线程持有,此时任何线程都可以获得该锁并调用,当有一个线程请求成功时,jvm会记录下持有锁的线程并将计数器设为1,此时其他线程请求该锁,则必须等待,当持有该锁的线程再次请求这个锁,重入后将计数器加1,当线程退出该锁,计数器就会递减,如果计数器为0时则释放锁。在使用分布式锁是会出现,在释放锁的时候如果前面的业务出问题了,导致释放锁没有执行,所以要对之前的业务逻辑进行异常捕获,并且将释放锁放到finally中,但是还会出现。

PeakGao 552

java同步锁的实现原理简单总结

ps:纯属个人简单总结,有不当之处或不合事实指出请指出,谢谢! java中同步锁使用synchronized实现,不管是作用在方法上还是代码快上,作用在方法上分静态方法和实例方法,作用在代码快上需要在后面添加锁的对象,实现原理是在作用对象的锁上添加拥有者,java每个对象头上可能包含monitor监视器锁,而锁上面标记了锁的拥有者,即一个线程,当线程进入同步块中时,检查锁对象中的owner是否已经有其他线程,如果是那么线程进入阻塞状态,进入等待队列,如果不是则修改对象锁的拥有者为当前线程,当线程退出同步块

cs13970021734的博客 802
上一篇: Spring Boot 自动配置原理分析
下一篇: java枚举学习
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值