多线程-线程终止

还不知道如何在java中终止一个线程?快来,一文给你揭秘 线程不能调用stop来终止主要是因为不会抛出异常,从而导致一些安全和数据不一致的问题。所以,最好的方式就是调用interrupt方法来处理。本文的例子。 阅读详情

1. interrupt()说明

在介绍终止线程的方式之前,有必要先对interrupt()进行了解。
关于interrupt(),java的djk文档描述如下:http://docs.oracle.com/javase/7/docs/api/

Interrupts this thread.
Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

Interrupting a thread that is not alive need not have any effect.

大致意思是:

interrupt()的作用是中断本线程。
本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
中断一个“已终止的线程”不会产生任何操作。

2. 终止线程的方式

Thread中的stop()和suspend()方法,由于固有的不安全性,已经建议不再使用!
下面,我先分别讨论线程在“阻塞状态”和“运行状态”的终止方式,然后再总结出一个通用的方式。

2.1 终止处于“阻塞状态”的线程

通常,我们通过“中断”方式终止处于“阻塞状态”的线程。
当线程由于被调用了sleep(), wait(), join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException异常。将InterruptedException放在适当的为止就能终止线程,形式如下:

@Override
public void run() {
    try {
        while (true) {
            // 执行任务...
        }
    } catch (InterruptedException ie) {  
        // 由于产生InterruptedException异常,退出while(true)循环,线程终止!
    }
}

说明:在while(true)中不断的执行任务,当线程处于阻塞状态时,调用线程的interrupt()产生InterruptedException中断。中断的捕获在while(true)之外,这样就退出了while(true)循环!
注意:对InterruptedException的捕获务一般放在while(true)循环体的外面,这样,在产生异常时就退出了while(true)循环。否则,InterruptedException在while(true)循环体之内,就需要额外的添加退出处理。形式如下:

@Override
public void run() {
    while (true) {
        try {
            // 执行任务...
        } catch (InterruptedException ie) {  
            // InterruptedException在while(true)循环体内。
            // 当线程产生了InterruptedException异常时,while(true)仍能继续运行!需要手动退出
            break;
        }
    }
}
 

说明:上面的InterruptedException异常的捕获在whle(true)之内。当产生InterruptedException异常时,被catch处理之外,仍然在while(true)循环体内;要退出while(true)循环体,需要额外的执行退出while(true)的操作。

2.2 终止处于“运行状态”的线程

通常,我们通过“标记”方式终止处于“运行状态”的线程。其中,包括“中断标记”和“额外添加标记”。
(01) 通过“中断标记”终止线程。
形式如下:

@Override
public void run() {
    while (!isInterrupted()) {
        // 执行任务...
    }
}

说明:isInterrupted()是判断线程的中断标记是不是为true。当线程处于运行状态,并且我们需要终止它时;可以调用线程的interrupt()方法,使用线程的中断标记为true,即isInterrupted()会返回true。此时,就会退出while循环。
注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。

(02) 通过“额外添加标记”。
形式如下:

private volatile boolean flag= true;
protected void stopTask() {
    flag = false;
}

@Override
public void run() {
    while (flag) {
        // 执行任务...
    }
}

 

 

说明:线程中有一个flag标记,它的默认值是true;并且我们提供stopTask()来设置flag标记。当我们需要终止该线程时,调用该线程的stopTask()方法就可以让线程退出while循环。
注意:将flag定义为volatile类型,是为了保证flag的可见性。即其它线程通过stopTask()修改了flag之后,本线程能看到修改后的flag的值。

 

综合线程处于“阻塞状态”和“运行状态”的终止方式,比较通用的终止线程的形式如下:

 

@Override
public void run() {
    try {
        // 1. isInterrupted()保证,只要中断标记为true就终止线程。
        while (!isInterrupted()) {
            // 执行任务...
        }
    } catch (InterruptedException ie) {  
        // 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
    }
}

 

3. 终止线程的示例

interrupt()常常被用来终止“阻塞状态”线程。参考下面示例:

 // Demo1.java的源码
 class MyThread extends Thread {
      
      public MyThread(String name) {
          super(name);
      }
  
      @Override
      public void run() {
         try {  
             int i=0;
             while (!isInterrupted()) {
                 Thread.sleep(100); // 休眠100ms
                 i++;
                 System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
             }
         } catch (InterruptedException e) {  
             System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
         }
     }
 }
 
 public class Demo1 {
 
     public static void main(String[] args) {  
         try {  
             Thread t1 = new MyThread("t1");  // 新建“线程t1”
             System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  
 
             t1.start();                      // 启动“线程t1”
             System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  
 
             // 主线程休眠300ms,然后主线程给t1发“中断”指令。
             Thread.sleep(300);
             t1.interrupt();
             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
 
             // 主线程休眠300ms,然后查看t1的状态。
             Thread.sleep(300);
             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
         } catch (InterruptedException e) {  
             e.printStackTrace();
         }
     } 
 }
  

运行结果:

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (TERMINATED) is interrupted now.
 

结果说明:
(01) 主线程main中通过new MyThread("t1")创建线程t1,之后通过t1.start()启动线程t1。
(02) t1启动之后,会不断的检查它的中断标记,如果中断标记为“false”;则休眠100ms。
(03) t1休眠之后,会切换到主线程main;主线程再次运行时,会执行t1.interrupt()中断线程t1。t1收到中断指令之后,会将t1的中断标记设置“false”,而且会抛出InterruptedException异常。在t1的run()方法中,是在循环体while之外捕获的异常;因此循环被终止。

我们对上面的结果进行小小的修改,将run()方法中捕获InterruptedException异常的代码块移到while循环体内。

  // Demo2.java的源码
  class MyThread extends Thread {
      
      public MyThread(String name) {
          super(name);
      }
  
      @Override
      public void run() {
         int i=0;
         while (!isInterrupted()) {
             try {
                 Thread.sleep(100); // 休眠100ms
             } catch (InterruptedException ie) {  
                 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
             }
             i++;
             System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
         }
     }
 }
 
 public class Demo2 {
 
     public static void main(String[] args) {  
         try {  
             Thread t1 = new MyThread("t1");  // 新建“线程t1”
             System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  
 
             t1.start();                      // 启动“线程t1”
             System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  
 
             // 主线程休眠300ms,然后主线程给t1发“中断”指令。
             Thread.sleep(300);
             t1.interrupt();
             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
 
             // 主线程休眠300ms,然后查看t1的状态。
             Thread.sleep(300);
             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
         } catch (InterruptedException e) {  
             e.printStackTrace();
         }
     } 
 }
 

运行结果:

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (RUNNABLE) loop 3
t1 (RUNNABLE) loop 4
t1 (RUNNABLE) loop 5
t1 (TIMED_WAITING) is interrupted now.
t1 (RUNNABLE) loop 6
t1 (RUNNABLE) loop 7
t1 (RUNNABLE) loop 8
t1 (RUNNABLE) loop 9
...
 

结果说明:
程序进入了死循环!
为什么会这样呢?这是因为,t1在“等待(阻塞)状态”时,被interrupt()中断;此时,会清除中断标记[即isInterrupted()会返回false],而且会抛出InterruptedException异常[该异常在while循环体内被捕获]。因此,t1理所当然的会进入死循环了。
解决该问题,需要我们在捕获异常时,额外的进行退出while循环的处理。例如,在MyThread的catch(InterruptedException)中添加break 或 return就能解决该问题。

下面是通过“额外添加标记”的方式终止“状态状态”的线程的示例:

  // Demo3.java的源码
  class MyThread extends Thread {
  
      private volatile boolean flag= true;
      public void stopTask() {
          flag = false;
      }
      
      public MyThread(String name) {
         super(name);
     }
 
     @Override
     public void run() {
         synchronized(this) {
             try {
                 int i=0;
                 while (flag) {
                     Thread.sleep(100); // 休眠100ms
                     i++;
                     System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
                 }
             } catch (InterruptedException ie) {  
                 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
             }
         }  
     }
 }
 
 public class Demo3 {
 
     public static void main(String[] args) {  
         try {  
             MyThread t1 = new MyThread("t1");  // 新建“线程t1”
             System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  
 
             t1.start();                      // 启动“线程t1”
             System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  
 
             // 主线程休眠300ms,然后主线程给t1发“中断”指令。
             Thread.sleep(300);
             t1.stopTask();
             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
 
             // 主线程休眠300ms,然后查看t1的状态。
             Thread.sleep(300);
             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
         } catch (InterruptedException e) {  
             e.printStackTrace();
         }
     } 
 }
 

运行结果:

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) loop 3
t1 (TERMINATED) is interrupted now.
 

 

4. interrupted() 和 isInterrupted()的区别

最后谈谈 interrupted() 和 isInterrupted()。
interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。
区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。

python中多线程如何用Ctrl+C终止 在编写python多线程程序时,发现用Ctrl+C无法终止,就算用Ctrl+Z强制当前进程转为后台并使之停止,而实际上并未停止,用ps命令一查还在运行,于是上网上找资料,动手实践,记录分享一下解决办法。 在你的代码中增加如下类 class Watcher(): def __init__(self): self.child = os.fork() if 阅读详情

相关推荐

Java进阶-第九篇:Java多线程 - 线程终止 - 异常终止

除了使用块,Java还提供了接口来实现自定义的异常处理策略。通过实现这个接口,可以为线程设置统一的异常处理逻辑,使异常处理更加灵活和集中。@OverrideSystem.out.println("线程 " + t.getName() + " 发生异常:" + e.getMessage());// 进行自定义的异常处理,如发送异常通知、记录详细日志等System.out.println("线程开始执行");// 这里会抛出ArithmeticException异常。

无 293

【JAVA】06使用interrupt()方法中断线程

使用interrupt()方法中断线程

qq_31448241的博客 2227

优雅终止Java多线程编程中的线程

在Java多线程编程中,正确地终止线程是一个重要的任务。线程的正确终止可以避免资源泄漏和不确定的行为。本文将介绍一些优雅地终止线程的方法,并提供相应的源代码示例。在编写多线程代码时,始终要注意线程的正确终止,以确保程序的稳定性和可靠性。通过正确地终止线程,可以确保线程的安全退出和资源的正确释放。在Java多线程编程中,优雅地终止线程是一个重要的问题。总结起来,优雅地终止Java多线程编程中的线程可以通过使用标志位、在上面的例子中,线程在执行过程中通过检查。在上面的例子中,线程在执行过程中通过调用。

AvskBug的博客 137

Java多线程系列--“基础篇”09之 interrupt()和线程终止方式

概要 本章,会对线程的interrupt()中断和终止方式进行介绍。涉及到的内容包括: 1. interrupt()说明 2. 终止线程的方式   2.1 终止处于“阻塞状态”的线程   2.2 终止处于“运行状态”的线程 3. 终止线程的示例 4. interrupted() 和 isInterrupted()的区别 转载请注明出处:http://www.cnblogs.

z69183787的专栏 1447

C语言多线程编程-线程终止

C语言多线程编程-线程终止

匠心码农 983

分布式推理服务(策略:数据并行、张量并行、流水线并行、专家并行)技术分析

摘要:分布式推理突破单GPU内存和算力限制,支持大模型部署。四种核心策略包括: 数据并行:复制模型拆分数据,提升吞吐但内存效率低; 流水线并行:模型分阶段执行,支持超大模型但存在计算气泡; 张量并行:拆分层内矩阵运算,延迟低但通信开销大; 专家并行:动态激活MoE模型专家,计算高效但路由复杂。实际应用中常组合策略(如3D并行),需权衡模型规模、硬件条件与性能目标。

技术引领业务创新 947

Thread--interrupt和线程终止

interrupt()的作用是中断本线程,本线程中断自己是被允许的,其他线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限,可能会抛出SecurityException异常。一、终止处于“阻塞状态”的线程:通过中断方式终止处于阻塞状态的线程,当线程由于被调用了sleep(),wait(),join()等方法而进入阻塞状态,此时调用线程的interrupt()将线

井底之蛙 1519

伺服电机的位置控制模式:PP模式与CSP模式的比较

在轮廓位置模式下,上位机给定目标绝对位置(或相对位置)、速度、加速度等参数,伺服内部的轨迹发生器根据这些参数生成目标位置曲线指令,通过伺服驱动器内部实现三环控制(位置环、速度环、力矩环)。如果还不理解,不妨看看下面的类比:轮廓位置模式中,上位控制器对伺服电机说,我给你个位置,怎么去哪,走那条路,那是你自己的事,这个时候伺服电机是有自己的思想的。同期同步位置模式红,上位机控制器对伺服电机的管理就和 严格,除了告诉你目标位置,走那条路都是控制器说的算,伺服电机就是个没有灵魂的工具。重点关注,轮廓位置模式。

m0_49968063的博客 5811

线程中断机制详解:interrupt() 真能杀掉线程吗?

Java线程中断机制详解:Thread.interrupt()并非强制终止线程,而是设置中断标志的协作式通知机制。正确响应中断的方式包括轮询isInterrupted()状态或处理InterruptedException异常。中断后需保留标志位,避免吞掉异常。Java废弃Thread.stop()是因强制终止会导致数据不一致等问题。最佳实践是编写可中断循环逻辑,妥善处理阻塞操作的中断异常。关键要理解中断是线程间的通信机制,体现Java"温和型并发控制"的协作哲学。

小健学Java的博客 1462

auboi5 + d435i 使用easy handeye进行标定时,遇到相关问题记录。

解决办法:在easy_handeye默认的 calibrate.launch 文件中move group 的默认值为 "manipulator",但针对每个人的机械臂,这个值需要更改。以aubo_i5为例,需要将其改为"manipulator_i5",或者自己编写launch文件时,传入参数move group的值,便能顺利解决问题!easy_handeye_calibration_server 进程出现崩溃。

weixin_61025092的博客 958

2025软考信息安全工程师真题

2025信息安全工程师软考真题

qq_40424393的博客 1710

多线程的终止方法(停止线程)

多线程的终止方法 多线程的终止共有3种方法 run方法执行结束,正常退出 异常终止 暴力终止 停止线程 停止线程是多线程中的一个重要技术点,本篇文章将对线程的停止操作进行详细讲解。 停止线程不能像for或while循环中使用break停止那样直接,而是需要一些技巧来终止。 如何判断线程已终止 Thread类中提供了两个方法: interrupted():测试当前线程是否已中断。(静态方法)...

citricl 5278

C++多线程强制终止

摘要:实际上,没有任何语言或操作系统可以为你提供异步突然终止线程的便利,且不会警告你不要使用它们。

华为云官方博客 4314

ARM功耗管理软件之DVFS&AVS

思考:功耗管理软件栈及示例?WFI&WFE?时钟&电源树?DVFS&AVS?

AI-安全-功耗 1233

YOLOv12改进策略【损失函数篇】| 替换Powerful-IoU(PIoU),增强对中等质量锚框的关注力,引导锚框更直接、高效地回归

本文针对现有基于IoU的损失函数在目标检测任务中存在的问题,提出了利用Powerful-IoU(PIoU)及其升级版PIoU v2改进YOLOv12的损失函数,以提高模型精度。文章详细介绍了PIoU和PIoU v2的结构原理,并提供了在YOLOv12中实现这些损失函数的具体步骤和代码修改方法。

Limiiiing的博客 931

Linux多线程之线程终止

主动终止(终止自己) 调用 return (void*)var; 调用void pthread_exit(void *value_ptr), 其它线程可以调用 pthread_join 获得这个针。 注: 这两个函数返回值都为指针类型,在线程中使用时所指向的地址必须为:malloc分配的或者全局变量;因为当线程退出是 其作用域的变量都将消失。 这两个函数的返回值都能通过 int pthrea...

Mark_404的博客 527

[案例四] 智能填写属性工具(支持装配组件还有建模实体属性的批量创建、编辑)

本次案例主要是对上次导出自动导出BOM的一个前处理,要想导出属性,首先的有属性。于是本着学习的态度进行制作,可能有些功能有点别扭(支持装配环境下组件属性的批量填写,支持建模环境下的实体(不包括装配组件实体)——为了减负我把其过滤掉了,一般没人会往装配体里添加进行建模吧,但是不免有奇葩,哈哈哈)。

weixin_47753171的博客 1983

多线程之两阶段终止模式

当某些线程正在运行时,如何去正确终止线程,如果直接强制终止线程(如调用不可控性两阶段终止模式(Two-Phase Termination Pattern)是一种。它的核心思想是:在终止线程前,先发出终止请求(第一阶段),然后线程在完成必要清理工作后再真正终止(第二阶段)。这种模式避免了直接强制终止线程导致的资源泄露、数据不一致等问题简单来说就是在一个线程T1中如何优雅的终止线程T2,给T2一个料理后事的机会sleep()wait()使用。

Gaomengsuanjia_的博客 1164

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Python多线程之线程创建和终止

python主要是通过thread和threading这两个模块来实现多线程支持。python的thread模块是比较底层的模块,python的threading模块是对thread做了一些封装,可以更加方便的被使用。Python threading模块不同于其他语言之处在于它没有提供线程的终止方法,本文分析了Python多线程终止控制的方法。

Adan 2万+

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

python 多线程终止子线程

python相关学习资料:https://edu.51cto.com/video/3502.htmlhttps://edu.51cto.com/video/4102.htmlhttps://edu.51cto.com/video/3832.htmlPython多线程终止子线程的探索之旅 在Python编程中,多线程是...

weixin_28716181的博客 216

android 蓝牙通讯demo

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

多线程编程-线程的创建和终止

1.引言: Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linux下pthread的实现是通过系统调用clone()来实现的。clone

红头发疯疯 372

【多线程】终止线程的方式、当中断失效、多线程的实现方式

1、Java终止线程的三种方式 使用退出标志,使程序正常退出(run方法完成后线程终止)。 使用stop()方法强行终止线程(被弃用) 为什么弃用stop? 1、调用 stop() 方法会立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。 2、调用 stop() 方法会立即释放该线程所持有的所有的锁,导致数据得不

历乱的日记簿 323

Linux:多线程---线程控制(线程创建&&线程等待&&线程终止)

本文介绍了线程控制相关内容,包括线程创建、等待和终止的实现方法。通过pthread_create函数创建线程时,参数包括线程ID、属性、执行函数和参数;线程等待使用pthread_join获取返回值;终止线程可通过return或pthread_exit实现。文章深入分析了原生线程库(如Linux的NPTL)的工作原理,指出线程控制块(TCB)存储在共享区,并通过clone系统调用实现轻量级进程。此外,还讨论了线程局部存储(__thread修饰符)和C++11线程库与原生库的关系。线程异常会导致整个进程终止,

知北游天的博客 2957
上一篇: 多线程-线程等待 (join)
下一篇: 设计模式-单例模式(Singleton)
Mark_XC
博客等级 码龄9年 16粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值