多线程安全问题

【C/C++】多线程安全问题的原因及解决方法 为了解决这些多线程安全问题,可以使用同步机制(如互斥量、信号量、条件变量)来协调线程之间的访问,或者使用并发编程模型(如锁机制、原子操作、并发数据结构)来保证数据的一致性和正确性。这些是常见的C++多线程编程中可能出现的安全问题,避免这些问题需要合理地使用同步机制,如互斥量(mutex)、条件变量(condition variable)、原子操作(atomic)等来保护共享数据的访问。的值,另一个线程对其进行写操作,由于没有进行同步,可能导致读取到不一致或无效的数据。上述代码中,两个线程同时对。 阅读详情

多线程安全问题

第一部分

1、多线程安全问题的描述

在卖票程序中多线程安全问题的描述:当4个线程在分别执行程序的时候,0线程进入程序进行判断tick>0;刚判断完条件,这时候0线程就卧倒下了,卧倒就是0线程具备执行的资格但是执行权被其他的线程给强走了,换句话说就是cpu切换到其他人那边去了;这是1线程就获得了执行权,1线程判断条件tick>0,是满足条件的,然后依次类推都是有可能出现这些情况,也就是说4个线程都已经卧倒了,但是这是cpu这是切换到了0线程身上了,这是0线程就直接向下执行了,这时0线程输出的票是1号票,但是这时1线程输出的是0号票,2线程输出的是-1号票,3线程输出的是-2号票,按照人们生活常理是没有0号票还负数的票的;-->这时程序就有可能会出现安全隐患;

2、用代码体现多线程安全问题

//实现Runnable
class Ticket implements Runnable
{
	private int tick =100;
//	Object obj = new Object();
	public void run()
	{
		while(true)
		{
//			synchronized(obj)
//			{
				if(tick>0)
				{
//					sleep方法抛出了异常
					try
					{
						Thread.sleep(10);
					}
					catch (Exception e)
					{
						System.out.println(e.toString());
					}
					System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
//				}
			}
		}
	}
}
class TicketDemo2
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
//		创建线程;需要传一个Ticket对象;要指定run方法所属对象;
//		Thread(Runnable target) 
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
//		分别要开启线程;
		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}
 

3、代码运行的结果

4、问题的描述

多线程最恐慌的就是安全问题,通过分析,发现出现了0和-1号票,因此就是多线程的运行出现了安全问题,

5、问题的原因

|--当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误。
|--如果一个线程进来把所有的语句都执行完,然后下一个线程在进来把程序执行完,那么就没有问题了。

6、解决办法

|--对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行;换句话说就是0线程在程序里面执行,1线程即使拿到了执行权也不让1线程执行;那么这样就靠谱了;
|--java对于多线程的安全问题提供了专业的解决方式;就是同步代码块;
|--如何判断哪些代码需要同步:就要看哪些代码在操作共享数据;-->tick就是共享数据;
//同步代码块
synchronized(对象)
{
	需要被同步的代码
}

7、代码体现

//实现Runnable
class Ticket implements Runnable
{
	private int tick =500;
//	需要放一个对象;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
//			同步代码块
			synchronized(obj)
			{
				if(tick>0)
				{
//					sleep方法抛出了异常	
					try
					{
						Thread.sleep(10);
					}
					catch (Exception e)
					{
						System.out.println(e.toString());
					}
					System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
				}
			}
		}
	}
}
class TicketDemo2
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
//		创建线程;需要传一个Ticket对象;要指定run方法所属对象;
//		Thread(Runnable target) 
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
//		分别要开启线程;
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		/*
		Ticket t1 = new Ticket();
		Ticket t2 = new Ticket();
		Ticket t3 = new Ticket();
		Ticket t4 = new Ticket();

		t1.start();
		t2.start();
		t3.start();
		t4.start();
		*/
	}
}
 

8、运行结果

现在就是4个线程共同操作一个共享数据,并且没有出现0和负数的情况;

9、同步原理

|--四个线程,这时有两个标志位,一个是0,一个是1,当0线程获取到cpu执行权时,判断标志位是1,进入同步代码快中,0线程进入以后,就把标志位恩恩1变成了0;把锁给关上了,然后0线程就判断if语句,满足条件,就开始读取,这时读取到了sleep语句,这是0线程就处于卧倒状态,这时1线程就进入懂啊了语句中,1线程就判断标志位是0,-->但是1线程是进不来的,这时0线程进醒了,就继续执行语句,出了同步,这是0线程就又做了一件事,就是把标志位的0置成了1;这时3线程抢到了执行权,这时3线程就把标志位改成了0;synchronized(obj)-->这个是一个锁,只有拿到了这个锁才可以进入到程序中执行,否则就一直等;
|--同步经典例子:火车上卫生间
|--对象如同锁,持有锁的线程可以在同步中执行;没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁;

10、同步前提

|--必须要有两个或者两个以上的线程;
|--必须是多个线程使用同一个锁;

11、同步好处和弊端

好处:解决了多线程的安全问题;
弊端:多个线程需要判断锁,较为消耗资源;

第二部分

1、同步函数的描述

需求:银行有个金库,有两个储户分别往里面存钱,每次存100,分别存3次;
问题的描述:当储户在存钱的时候,第一个储户出了100元,但是没有立即执行输出语句,而是卧倒了,这是又来了一个储户,来存钱,存了100后,变成了200元,这时就执行了输出语句,打印了200元,而没有打印100;这就是出现了安全问题;
如何找问题:
|--明确哪些代码是多线程运行代码;
|--明确共享数据;
|--明确多线程运行代码中,哪些语句是操作的是共享数据;

2、同步函数

同步有两种表现形式:
|--同步代码:把同步放在代码中;
|--同步函数:把同步作为修饰符放在函数上;

3、同步函数的锁是this

|--如果卖票的程序在函数上使用同步的话,那么是不OK的,这样输出的是只有0线程一直在运行,把锁就一直锁起来了,其他的线程就没有运行;
|--同步函数用的是哪一个锁呢?
|--函数需要被对象调用,那么函数都有一个所属对象应用,就是this,所以同步函数使用的锁是this;

验证:

|--当同步代码块里面的锁的对象是obj的时候,同步函数里面的锁是this的时候,运行的结果是线程0和线程1交替运行,而且还出现了不同步的现象;

|--当同步代码块里面的锁的对象是this的时候,同步函数里面的锁是this的时候,运行的结果只有线程1运行;

|--由此可见,同步函数里面的锁是this锁;

4、代码体现

//卖票的程序
//线程0是同步代码块,线程1是同步函数;
class Ticket implements Runnable
{
	private int tick =100;
	Object obj = new Object();
//	boolean型的标记
	boolean flag = true;
	public void run()
	{
//		是true的话
		if(flag)
		{
			while(true)
			{
//			同步代码块
				synchronized(this)
				{
					if(tick>0)
					{
						try
						{
							Thread.sleep(10);
						}
						catch (Exception e)
						{
							System.out.println(e.toString());
						}
//						同步代码快
						System.out.println(Thread.currentThread().getName()+"--code--"+tick--);
					}
				}
			}
		}
//		如果是false的话
			else
				while(true)
					show();
	}
//	同步函数
	public synchronized void show()//这个锁是this;
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch (Exception e)
			{
				System.out.println(e.toString());
			}
//			同步函数
			System.out.println(Thread.currentThread().getName()+"--show--"+tick--);
		}
	
	}
}
class ThisLockDemo
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
//		Thread t3 = new Thread(t);
//		Thread t4 = new Thread(t);

		t1.start();
//		让主线程停10毫秒
		try
		{
			Thread.sleep(10);
		}
		catch (Exception e)
		{
			System.out.println(e.toString());
		}
//		把flag改为false,并开启t2
		t.flag =false;
		t2.start();
//		t3.start();
//		t4.start();
	}
}
 

5、运行结果

由此可见,同步函数用的是this锁;

第三部分

1、同步静态函数描述

|--通过验证法发现同步静态函数出现了不同步的现象;也就是说静态函数用的锁不是this锁;其实静态方法中是没有this的,因为静态中是没有对象的;

|--静态静内存后,内存中没有本类对象,但是一定有该类所属的字节码文件对象;

|--格式是:类名.class;该对象的类型是Class;

2、通过代码展现

class Ticket implements Runnable
{
	private static int tick =100;
	Object obj = new Object();
	boolean flag = true;
	public void run()
	{
		if(flag)
			while(true)
			{
//			该类所属的字节码文件对象;
				synchronized(Ticket.class)
				{
					if(tick>0)
					{
						try
						{
							Thread.sleep(10);
						}
						catch (Exception e)
						{
							System.out.println(e.toString());
						}
						System.out.println(Thread.currentThread().getName()+"--code--"+tick--);
					}
				}
			}
			else
				while(true)
					show();
	}
	public static synchronized void show()
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch (Exception e)
			{
				System.out.println(e.toString());
			}
			System.out.println(Thread.currentThread().getName()+"--show--"+tick--);
		}
	
	}
}
class StaticMethodDemo
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();
		try
		{
			Thread.sleep(10);
		}
		catch (Exception e)
		{
			System.out.println(e.toString());
		}

		t.flag =false;
		t2.start();
	}
}
 

3、运行结果

当在静态同步函数,发现其的对象不是this锁了,而是该类所属的字节码文件对象,类名.class;

第四部分

我的总结

|--在同步代码块里面用的锁是任意对象,
|--在同步函数里面用的锁是this;
|--在静态同步函数上用的锁是该类所属的字节码文件对象,格式:类名.class;
|--想要解决多线程的安全问题,就要先明确同步的两个前提:是不是有多个线程,是不是用的是同一把锁;
在不同版本Python中安装PyQT5及PyQT5-Tools 阅读详情

相关推荐

医学图像处理入门:如何用Python将CT灰度图转换为伪彩色图像(附代码)

本文详细介绍了如何使用Python将CT灰度图转换为伪彩色图像,提升医学图像的可读性和诊断效率。通过OpenCV和Matplotlib等工具,实现灰度值到彩色空间的映射,特别适用于突出显示特定组织密度差异。文章包含完整代码示例和临床实用技巧,适合医学影像分析初学者和Python开发者。

gitlab7runner的博客 327

黑马程序员——多线程——多线程概述,实现,控制与安全问题的解决

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 一.多线程 1.多线程:一个应用程序有多条执行路径 A:概述 进程:当前正在执行的程序,代表一个应用程序在内存中的执行区域 线程:是依赖于进程的执行绪(执行路径/控制单元),是程序使用CPU的基本单位。 单线程:一个进程中,只有一个线程执行。 多线程:同一个进程

hison_lian的博客 481

STM32项目实战_基于STM32F103的智能药箱设计与实现(硬件详解+软件逻辑+调试要点)

本文详细介绍了基于STM32F103的智能药箱设计与实现,涵盖硬件设计、软件逻辑和调试要点。项目核心功能包括精准提醒、药品管理和远程监控,采用STM32单片机作为主控,结合光电开关、蓝牙模块等实现智能化管理。文章还分享了电路图设计、程序优化及常见问题解决方案,为嵌入式开发爱好者提供实用参考。

weixin_28717683的博客 306

黑马程序员学习笔记七——多线程

----------------------android培训、java培训、期待与您交流! ---------------------- 多线程 1.初识线程 进程和线程区别 进程是一个正在执行的程序,每一个进程执行都有一个执行顺序。该顺序是一个执 行路径,或者称之为一个控制单元。 线程就是进程中一个独立的控制单元。线程控制进程的执行。 所以一个进程中至少有一个线

huchuan90的专栏 723

从信号到振镜:STM32F103 + XY2-100协议 + AM26LS31芯片的激光打标/雕刻系统信号链搭建指南

本文详细介绍了基于STM32F103微控制器和XY2-100协议的激光打标/雕刻系统信号链搭建方法。从数字振镜控制协议解析、AM26LS31差分驱动电路设计到信号完整性优化,提供了一套完整的解决方案,帮助工程师实现高精度激光控制系统的开发与调试。

weixin_42536058的博客 237

数学建模国赛C题实战:从数据预处理到模型优化的完整流程(附Python代码)

本文系统讲解了数学建模国赛C题的完整实战流程,涵盖从数据预处理、模型选择与构建到优化与结果可视化的关键步骤。文章重点分享了如何利用Python进行高效的数据清洗、特征工程,并提供了多种机器学习模型的实现代码与对比验证方法,旨在帮助参赛者构建逻辑清晰、结果可靠的解决方案。

weixin_29079643的博客 295

黑马程序员<java基础<多线程同步异步问题>>

多线程 进程:正在进行中的程序 线程:就是进程中一个执行单元或执行情景或执行路径负责进程中程序执行的控制单元 一个进程中至少要有一个线程 当一个进程中线程有多个时,就是多线程 多线程解决的问题:可以让多部分代码同时执行。 什么是同时执行呢?其实都是cpu在瞬间做着快速切换完成的。 cpu的切换随机的,按照时间片的方式来完成。 其实java运行就是多线程的

rina_love__story的博客 471

最小二乘法(看完即会)

其中,X是数据矩阵,每一行是一个数据点,每一列是一个自变量(包括截距项),Y是观测值向量,β是待求的系数向量。为了找到使S最小的a和b的值,我们需要对S求关于a和b的偏导数,并令它们等于零。最小二乘法的数学公式通常用于线性回归问题,其中目标是找到一条直线(或更高维度的超平面),使得这条直线与给定数据点的误差平方和最小。同样地,通过求偏导数和令偏导数等于零,我们可以得到一个线性方程组,解这个方程组可以找到最优的b_0, b_1, …+ b_m*x_m,其中b_0是截距,b_1到b_m是各个自变量的系数。

qlkaicx的博客 2万+

java黑马头条项目,Java系列学习进阶视频

专题5:Java序列化 1、什么是java序列化,如何实现java序列化? 2、保存(持久化)对象及其状态到内存或者磁盘 3、序列化对象以字节数组保持-静态成员不保存 4、序列化用户远程对象传输 5、Serializable 实现序列化 6、writeObject 和 readObject 自定义序列化策略 7、序列化 ID 8、序列化并不保存静态变量 9、Transient 关键字阻止该变量被序列化到文件中 10、序列化(深 clone 一中实现) 专题6:J

m0_56662269的博客 644

华为EC6110系列(3798mv310芯片)强刷固件全解析:从准备到实战

本文详细解析了华为EC6110系列(3798mv310芯片)强刷固件的全过程,从准备工作到实战步骤,包括固件特点、刷机工具准备、详细刷机步骤及常见问题解决方案。适合想要提升盒子性能的用户参考,特别强调了刷机过程中的注意事项和实用技巧。

rock5的博客 734

ZEMAX知识点:坐标间断面(coordinate break)

zemax知识点:坐标间断面

weixin_47638568的博客 1万+

悲观锁和乐观锁

悲观锁和乐观锁 悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。 常用的就是代码块或方法前加synchronized关键字和ReentrantLock。 synchronized自动解锁,Lock需求手动解锁,并且必须在finally中释放,否则会引起死锁。 乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。 常用的方法就是Ver

weixin_41359780的博客 300

LoRa学习:LoRa通信调制解调的实现原理与性能

LoRa学习:LoRa调制解调原理与性能 1、LoRa调制解调器原理 LoRa调制解调器采用专利扩频调制和前向纠错技术。与传统的FSK、OOK调制技术相比,LoRa扩大了无线通讯链路的覆盖范围(实现了远距离无线传输),提高了链路的鲁棒性。。 开发人员可调整扩频因子和纠错率这两个参数,从而平衡通讯时的带宽占用、通信速率、空中包的存活时间、以及抗干扰性等。。 LoRa调制解调器...

HowieXue 薛永浩的博客 3万+

多线程引发的安全问题

上一章我们介绍了线程的一些基础知识点,例如创建线程、查看线程、中断线程、等待线程等知识点,今天我们讲解多线程下引发的安全问题线程安全(最复杂也最重要)产生线程安全问题的原因锁synchronized 的特性互斥性刷新内存可重入死锁(重要)如果各位对文章的内容感兴趣的话,请点点小赞,关注一手不迷路,讲解的内容我会搭配我的理解用我自己的话去解释如果有什么问题的话,欢迎各位评论纠正

N_0050的博客 1214

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【多线程】线程安全问题

本文详细探讨了多线程编程中的线程安全问题及其解决方案。线程安全是指程序在多线程环境下能够正确处理共享资源,确保数据的完整性和一致性。线程不安全的原因主要包括线程间的执行方式、多个线程修改同一个变量、操作的非原子性、内存可见性问题以及指令重排序问题。为了解决线程不安全问题,文章介绍了锁机制,通过`synchronized`关键字实现对共享资源的互斥访问,确保同一时间只有一个线程能操作临界区资源。文章还讨论了死锁的产生条件及解决方法,如规定加锁顺序、引入计数器等。此外,`volatile`关键字用于解决内存

我的博客 6593

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Java多线程并发安全问题

当多个线程并发操作同一临界资源,由于线程切换时机不确定,导致操作临界资源的顺序出现混乱严重时可能导致系统瘫痪.临界资源:操作该资源的全过程同时只能被单个线程完成.

是程序喵呀 1201

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

多线程——安全问题

多线程运行可能出现安全问题   以买票程序为例,共有100张票,四个窗口同时买票。 class Ticket implements Runnable { private int tick = 100; public void run() { while(true) { if(tick > 0) { try { Thread.sleep(

满天星的专栏 415

JAVA中的多线程安全问题及解决方案

线程安全是指在多线程环境下,对共享资源进行并发访问时,程序能够正确地处理,不会出现数据不一致、逻辑错误等问题,确保程序的执行结果与单线程环境下的执行结果相同,或者符合预期的并发逻辑。有些代码在多线程环境执行下会出现问题,这样的问题就称为线程不安全原子性指的是一个操作是不可中断的,要么全部执行,要么都不执行。在多线程环境下,如果多个线程同时修改共享变量,可能会导致数据不一致。比如 i++ 这样的操作,虽然看起来是一条语句,但实际上分为读取、增加和写入三个步骤,这就不是原子的。

weixin_65528013的博客 1696

多线程编程的安全问题及其解决方法

经过了专栏的前几期的讲解,想必大家已经对多线程有了一些概念了,很多朋友一定已经想要进行多线程编程了。所以,我们开始第一步,运行上述代码,可以得到如下结果:大家的结果不一定是这个数字,但不会是正常应该得到的 1_000_000。那么这是为什么呢?为了更好的帮助大家进行理解,我要介绍关于的概念。表示一个操作是的,即该操作要么,要么。上述的代码无法得到应有的结果,就是因为 count++ 的操作不是一个具有原子性的操作。这三个操作可能会因为并发执行被随意地打断。

dalaoyuzuiniu的博客 2124

JAVA多线程的安全问题与解决办法

最后,为了提高性能,我们需要减少锁竞争、消除不必要的锁、进行锁粗化等优化。解决办法:使用原子类(如AtomicInteger、AtomicLong)或锁(如synchronized、ReentrantLock)保证操作的原子性。问题描述:在某些情况下,编译器可能会自动消除不必要的锁,但有时候程序员编写的不必要的同步代码会影响性能。解决办法:确保所有线程都能公平地获取到资源,可以使用公平锁(如ReentrantLock的公平模式)。解决办法:避免循环等待、避免资源不可抢占、避免持有多个锁、按顺序获取锁。

qa3629723的博客 907

多线程并发安全问题详解

定义: 当多个线程同时执行,多个线程之间是相互抢占资源执行,并且抢占是发生在线程的执行的每一步过程中,导致出现非法数据。这种现象就称之为多线程的并发安全问题。 代码案例: public class SellTicketDemo { public static void main(String[] args) { //创建票对象 Ticket t=new Ticke...

帅成一匹马 4654

ArrayList源码和多线程安全问题分析

文章目录使用 Arrays.asList 把数据转换为List的三个坑坑一:不能直接使用Arrays.asList来转换基本类型数组坑二:Arrays.asList 返回的List不支持增删操作坑三:对原始数据的修改会影响到我们获取到的List总结 使用 Arrays.asList 把数据转换为List的三个坑 坑一:不能直接使用Arrays.asList来转换基本类型数组 我们来看一个把基本数组类型转List的示例: 如下代码,我们初始化一个int[]数组,然后使用Arrays.asList 把数组转换为

半夏_2021的博客 553

多线程的安全问题

多线程安全问题产生的原因 1. 产生的前提 1> 多个线程操作共享的数据; 2> 操作共享数据的代码超过1条; 2. 产生的原因 当一个线程在执行操作共享数据的多条代码的过程中,其它线程也参与了运算,就会导致线程安全问题产生。 class TicketRunnable implements Runnable { private int num = 100; public void

张超的博客 685

如何解决多线程的线程不安全问题

解决线程安全的思路和方法;1,使用没有共享资源的模型2.不对共享变量进行写操作3.通过加锁和一些原子操作来保证线程的顺序,可见性,和原子性。

huapiaoy的博客 1944

多线程为什么会出现安全问题

# <center>一、多线程为什么会出现安全问题</center> 为什么多线程在执行的时候会造成安全问题呢,下面我们来了解一下所谓的线程安全倒地时怎么来的。 ## 一、内存模型简述 > 了解的同学都知道java内存模型被分为了五个区域,程序计数器、堆、虚拟机栈、本地方法栈以及方法区,理论上方法区也是属于堆中的一部分,只不过方法区是堆中的一块永久区域,也就是垃圾回收不是很频繁,但绝不是不进行垃圾回收,而堆中的垃圾回收则相对频繁的进行,我们稍微来看一下五个区域的作用,下面上网

chenhuanjing的博客 1785
上一篇: Java-多线程
下一篇: 多线程(死锁)
fyldragonfyl
博客等级 码龄12年 9粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值