SwingUtilities中invokeLater

【Java线程】Swing事件分发线程EDT与SwingUtilities.invokeLater 在Swing程序中,经常能看到如下这种代码: SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { textField1.setText("element changed!"); textField1.setForeGround(Color.RED 阅读详情

      在Java中Swing是线程不安全的,是单线程的设计,这样的造成结果就是:只能从事件派发线程访问将要在屏幕上绘制的Swing组件。事件派发线程是调用paint和update等回调方法的线程,它还是事件监听器接口中定义的事件处理方法,例如,ActionListener中的actionPerformed方法在事件派发线程中调用。

       Swing是事件驱动的,所以在回调函数中更新可见的GUI是很自然的事情,比如,有一个按钮被按下,项目列表需要更新时,则通常在与该按钮相关联的事件监听器的actionPerformed方法中来实现该列表的更新,从事件派发线程以外的线程中更新Swing组件是不正常的。

       有时需要从事件派发线程以外的线程中更新Swing组件,例如,在actionPerformed中有很费时的操作,需要很长时间才能返回,按钮激活后需要很长时间才能看到更新的列表,按钮会长时间保持按下的状态只到actionPerformed返回,一般说来耗时的操作不应该在事件处理方法中执行,因为事件处理返回之前,其他事件是不能触发的,界面类似于卡住的状况,所以在独立的线程上执行比较耗时的操作可能更好,这会立即更新用户界面和释放事件派发线程去派发其他的事件。

      SwingUtilities类提供了两个方法:invokeLate和invoteAndWait,它们都使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。

   只有从事件派发线程才能更新组件。

程序示例:更新组件的错误方法
startButton.addActionListener(new ActionListener())
{
   public void actionPerformed(ActionEvent e) 
   {
    GetInfoThread t = new GetInfoThread(Test.this);
    t.start();
    startButton.setEnabled(false);
   }
}

class GetInfoThread extends Thread 
{
Test applet;
public GetInfoThread(Test applet) 
{
this.applet = applet;
}

public void run() 
{
   while (true) 
   {
    try 
    {
     Thread.sleep(500);
     applet.getProgressBar().setValue(Math.random() * 100);
    } 
    catch (InterruptedException e) 
    {
     e.printStackTrace();
    }
   }
}
}

错误分析:在actionPerformed中,监听器把按钮的允许状态设置为false,由于是在事件派发线程上调用actionPerformed,所以setEnabled是一个有效的操作,但是在GetInfoThread中设置进度条是一个危险的做法,因为事件派发线程以外的线程更新了进度条,所以运行是不正常的。

   1、invokeLater使用
class GetInfoThread extends Thread 
{
Test applet;

Runnable runx;

int value;

public GetInfoThread(final Test applet) 
{
   this.applet = applet;
   runx = new Runnable() 
   {
     public void run() 
     {
     JProgressBar jpb = applet.getProgressBar();
     jpb.setValue(value);
     }
   }
}

   public void run() 
   {
     while (true) 
     {
        try 
        {
         Thread.sleep(500);
         value = (int) (Math.random() * 100);
         System.out.println(value);
         SwingUtilities.invokeLater(runx);
        } 
        catch (InterruptedException e) 
        {
         e.printStackTrace();
        }
     }
    }
}
   
   2、invokeAndWait
   与invoikeLater一样,invokeAndWait也把可运行对象排入事件派发线程的队列中,invokeLater在把可运行的对象放入队列后就返回,而invokeAndWait一直等待知道已启动了可运行的run方法才返回。如果一个操作在另外一个操作执行之前必须从一个组件获得信息,则invokeAndWait方法是很有用的。
   
   class GetInfoThread extends Thread 
   {
   Runnable getValue,setValue;
   int value,currentValue;
   public GetInfoThread(final Test applet)
   {
    getValue=new Runnable()
    {
     public void run()
     {
      JProgressBar pb=applet.getProgressBar();
      currentValue=pb.getValue();
      }
    };
   setValue=new Runnable()
   {
    public void run()
     {
      JProgressBar pb=applet.getProgressBar();
      pb.setValue(value);
     }
}
   }
   
   public void run()
   {
    while(true)
    {
    try
    {
      Thread.currentThead().sleep(500);
      value=(int)(Math.random()*100);
      try
      {
      SwingUtilities.invokeAndWait(getValue);//直到getValue可运行的run方法返回后才返回
      }
      catch(Exception ex)
      {
       
      }
       if(currentValue!=value)
       {
       SwingUtilities.invokeLater(setValue);
       }
     }
      catch(Exception ex)
      {
      }
     }
   }
      invokeLater和invoikeAndWait的一个重要区别:可以从事件派发线程中调用invokeLater,却不能从事件派发线程中调用invokeAndWait,从事件派发线程调用invokeAndWait的问题是:invokeAndWait锁定调用它的线程,直到可运行对象从事件派发线程中派发出去并且该可运行的对象的run方法激活,如果从事件派发线程调用invoikeAndWait,则会发生死锁的状况,因为invokeAndWait正在等待事件派发,但是,由于是从事件派发线程中调用invokeAndWait,所以直到invokeAndWait返回后事件才能派发。
      
      actionPerformed();返回的时候事件派发线程才能派发线程,而在actionPerformed中使用invokeAndWait则会导致actionPerformed不能返回。所以也就无法派发invokeAndWait中的线程。

      由于Swing是线程不安全的,所以,从事件派发线程之外的线程访问Swing组件是不安全的,SwingUtilities类提供这两种方法用于执行事件派发线程中的代码

总结: GUI中多线调用方法应该使用:SwingUtilities.invokeLater和invokeAndWait 而不是普通情况下那样应用.


看到很多地方讲述Swing中的并发和多线程问题,感觉讲的都不如Sun的教程,这里复述一下关键。Swing之所以和多线程紧密联系在一起是因为图形界面编程中如果只采取顺序编程(也就是你的代码或任务依次执行),会出现很大的问题,比如你要编写一个FTP客户端,你不能让文件下载的时候,用户界面死在那里,你既不能取消任务也不能和界面交互吧。所以有必要将耗时的任务,比如文件下载放到一个独立的线程中处理,而让用户同时能够干其他事情。简单来说,Swing中有三种线程:

启动线程或者初始线程: 这个线程负责调用main方法,很多顺序编程一开始就用的是这种线程。在Swing中启动线程负责很少的事务,主要干两件事情,第一件就是创建一个可运行的对象(Runnable Object),这个可运行对象的任务比较重要,它负责初始化图形界面,第二件就是将这个可运行对象安排到另外一个非常重要的线程,事件分派线程中执行。第二件事情是通过SwingUtilies的invokeLater和invokeAndWait方法来实现的。几乎所有的创建Swing组件和与Swing组件交互的代码都要在事件分派线程中执行。 
事件分派线程:在Swing中负责事件处理的代码需要在一个特定的线程中运行,这个线程就是事件分派线程。大部分调用Swing方法的代码也在这个线程中运行。原因是大部分Swing对象中的方法并不是线程安全的,所以需要这个特定的事件分派线程来保证线程安全。当然也有部分swing对象中的方法指明是线程安全的,这些方法可以在任何线程中调用。你可以将事件分派线程中运行的代码想象成一系列短小的任务,大部分任务都是调用事件处理方法,例如ActionListener.actionPerformed()方法,其他任务可被程序代码通过SwingUtilities的invokeLater/invokeAndWait方法来安排。需要注意的是,在事件分派线程中的任务必须短小精悍,这意味着这些任务能够很快执行完毕,如果你发现有一个耗时的任务,那么你肯定出错了,你会发现你的图形界面经常被卡住,或者死掉了。对于耗时任务你需要另外一个线程,例如工作线程(Worker Thread)来处理。判断你的代码时候运行在事件分派线程上的方法很简单,使用javax.swing.SwingUtilities.isEventDispatchThread()方法即可。 
工作线程(Worker Thread)或者后台线程(Background Thread):你可以在这个线程中处理耗时任务。

 

 

 

 

 

如何使用线程
  Java平台从开始就被设计成为多线程环境。在你的主程序执行的时候,其它作业如碎片收集和事件处理则是在后台进行的。本质上,你可以认为这些作业是线程。它们正好是系统管理线程,但是无论如何,它们是线程。线程使你能够定义相互独立的作业,彼此之间互不干扰。系统将交换这些作业进或出CPU,这样(从外部看来)它们好象是同时运行的。
  
  在你需要在你的程序中处理多个作业时,你也可以使用多个进程。这些进程可以是你自己创建的,你也可以操纵系统线程。
  
  你进行这些多作业处理,要使用几个不同的类或接口:
  
  java.util.Timer类 
  javax.swing.Timer类 
  Thread类 
  Runnable接口 
  对于简单的作业,通常需要重复的,你可以使用java.util.Timer类告诉它“每半秒钟做一次”。注意:大多数系统例程是使用毫秒的。半秒钟是500毫秒。
  
  你希望Timer实现的任务是在java.util.TimerTask实例中定义的,其中运行的方法包含要执行的任务。这些在Hi类中进行了演示,其中字符串“Hi”重复地被显示在屏幕上,直到你按Enter键。
  
  import java.util.*; public class Hi {  public static void main(String args[])     throws java.io.IOException {   TimerTask task = new TimerTask() {    public void run() {     System.out.println("Hi");    }   };   Timer timer = new Timer();   timer.schedule(task, 0, 500);   System.out.println("Press ENTER to stop");   System.in.read(new byte[10]);   timer.cancel();  }} 
网管论坛bbs_bitsCN_com

  
  Java Runtime Environment工作的方式是只要有一个线程在运行,程序就不退出。这样,当取消被调用,没有其它线程在运行了,则程序退出。有一些系统线程在运行,如碎片收集程序。这些系统线程也被称为后台线程。后台线程的存在不影响运行环境被关闭,只有非后台线程保证运行环境不被关闭。
  
  Javax.swing.Timer类与java.util.timer类的工作方式相似,但是有一些差别需要注意。第一,运行的作业被ActionListener接口的实现来定义。第二,作业的执行是在事件处理线程内部进行的,而不象java.util.Timer类是在它的外部。这是很重要的,因为它关系到Swing组件集是如何设计的。
  
  如果你不熟悉Swing,它是一组可以被Java程序使用的图形组件。Swing被设计程被称为单线程的。这意味着对Swing类内部内容的访问必须在单个线程中完成。这个特定的线程是事件处理线程。这样,例如你想改变Label组件的文字,你不能仅仅调用Jlabel的setText方法。相反,你必须确认setText调用发生在事件处理线程中,而这正是javax.swing.Time类派的上用场的地方。
  
  为了说明这第二种情况,下面的程序显示一个增加的计数器的值。美半秒钟计数器的数值增加,并且新的数值被显示。 中国网管论坛bbs.bitsCN.com 
  
  import javax.swing.*;import java.awt.*;import java.awt.event.*; public class Count {  public static void main(String args[]) {   JFrame frame = new JFrame();   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   Container contentPane = frame.getContentPane();   final JLabel label = new JLabel("", JLabel.CENTER);   label.setFont(new Font("Serif", Font.PLAIN, 36));   contentPane.add(label, BorderLayout.CENTER);   ActionListener listener = new ActionListener() {    int count = 0;    public void actionPerformed(ActionEvent e) {     count++;     label.setText(Integer.toString(count));    }   };   Timer timer = new Timer(500, listener);   timer.start();   frame.setSize(300, 100);   frame.show();  }}
  
  上述程序的结果是:
  [[The No.1 Picture.]]
  万一你要做的不是一个简单的重复作业,java.lang.Thread类就派上了用场。它允许你自己控制基本功能。通过创建Thread的一个子类,你可以使你的系统脱离,并进行一个长时间运行的作业,如从网络上读取一个文件,而不阻碍你的其它程序的运行。这种长时间运行的作业将在run方法中定义。 网管网www_bitscn_com 
  
  第二种方式是创建Thread类的子类并在子类中实现run方法,或在实现runnable的类中实现run方法,并将这个实现传递给Thread的构造函数。
  
  你可能会问有什么区别。Java编程语言仅支持单一继承。如果你设计的调用是除了Thread以外的其它类,你可以是你的类实现Runnable,而它可以是你的作业被执行。否则,你定义Thread的子类来运行你的Run方法,在处理过程中不再添加其它操作。
  
  对于创建Thread子类的第三种情况,下面的程序生成了一个新的线程来计算一个特定URL的字符数,这个URL是通过命令行传递进来的。在这进行过程之中,实现Runnable的第四种情况被演示,打印出重复的消息。注意在实现Runnable的这后一种情况下,你必须提供重复消息的代码。你必须同时sleep,以分配时间并完成操作。在两种情况下,与使用Timer相比较。这段程序的最后一部分包含有你从命令行读取命令以触发程序结束。注意在系统读取URL并打印消息的同时,你总可以按Enter键结束程序。
  
  import java.io.*;import java.net.*; public class Both {  public static void main(String args[]) {   final String urlString = args[0];   final String message = args[1];   Thread thread1 = new Thread() {    public void run() {     try {      URL url = new URL(urlString);      URLConnection connection =        url.openConnection();      InputStreamReader isr = new        InputStreamReader(          connection.getInputStream());      BufferedReader reader = new BufferedReader(isr);      int count = 0;      while (reader.read() != -1) {       count++;      }      System.out.println("Size is : "        + count);      reader.close();     } catch (MalformedURLException e) {      System.err.println("Bad URL: "        + urlString);     } catch (IOException e) {      System.err.println("I/O Problems");     }    }   };   thread1.start();   Runnable runnable = new Runnable() {    public void run() {     while(true) {      System.out.println(message);      try {       Thread.sleep(500);      } catch (InterruptedException e) {      }     }    }   };   Thread thread2 = new Thread(runnable);   thread2.start();   try {    System.out.println("Press ENTER to stop");    System.in.read(new byte[10]);   } catch (IOException e) {    System.out.println("I/O problems");   }   System.exit(0);  }} 网管u家u.bitsCN.com 
  
  因为有多种方式来处理线程,你选用哪种技术取决于你和你面临的条件。要成为一个有效的Java编程人员,尽管你通常不必学习Java编程语言的所有内容和核心库,但是线程是一个例外。你越早了解线程如何工作和如何使用线程,你将越早了解Java程序如何工作和交互

Swing 线程之SwingUtilities.invokeLater()  现在我们要做一个简单的界面。包括一个进度条、一个输入框、开始和停止按钮。需要实现的功能是:当点击开始按钮,则更新进度条,并且在输入框内把完成的百分比输出(这里只做例子,没有真正去做某个工作)。 代码1:import java.awt.FlowLayout;import java.awt.event.ActionEvent;import java.awt.eve 阅读详情

相关推荐

SwingUtilities的invokeLater和invokeAndWait

Swing程序的线程处理 前言 因为很多人会见到一些源代码中调用SwingUtilities的invokeLater或者invokeAnd-Wait方法,但是却不理解它们到底起到了什么作用,本文的目标就是让你理解这两个方法的意义。本文是swing编程基础且不可越过的一节。而且,如你所见,本文的副标题是“Swing程序的线程处理”,其实这是对本文内容更好的概括。 事件派

yanwushu的博客 7328

Java Swing线程之SwingUtilities.invokeLater解释

转自http://www.codeceo.com/article/swing-swingutilities-invokelater.html 译者注:本文其实是一个各方见解评论的总结,已将其中于此处发布不妥的内容去掉,全文内容可查看原文。 在官方的文档里:http://docs.oracle.com/javase/tutorial/uiswing/painting/step1.html告诉我...

weixin_30566063的博客 1330

还在用iReport 5.6.0?手把手教你用JasperSoft Studio 6.8.0设计报表(附JDK 11+兼容方案)

本文详细介绍了从iReport 5.6.0迁移到JasperSoft Studio 6.8.0的全过程,包括JDK 11+环境配置、项目迁移实战、高级特性应用及常见问题解决方案。帮助开发者高效完成报表生成工具的升级,提升开发体验和报表性能。

weixin_30687051的博客 407

Java的invokeLater

是 Java Swing 库中的一个方法,用于确保一个任务可以在稍后的某个时间在事件分发线程(Event Dispatch Thread, EDT)上执行。在 Swing 应用程序中,所有的组件更新和操作通常需要在这个线程上完成,以避免线程安全问题。是一个实用的工具,用于确保代码块在 Swing 的事件分发线程上异步执行,这对于维护 Swing 应用程序的线程安全至关重要。这个程序的功能与之前的示例相同,只是使用了 lambda 表达式来替代匿名内部类,使代码更加简洁。在这个例子中,我们创建了一个。

weixin_63490470的博客 2310

瑞芯微RK809音频驱动

瑞芯微RV1126中的RK809音频驱动开发

m0_61899715的博客 5221

避坑指南:Allegro 17.2更新封装时遇到的5个典型问题及解决方案(含Symbol报错处理)

本文深度解析Allegro 17.2封装更新过程中的核心机制与常见陷阱。针对“Update Symbols”功能失效、焊盘不匹配、DRC报错、图形层叠错误等5大典型问题,提供从原理分析到实战解决方案的完整指南,帮助用户实现从“更新失败”到“丝滑替换”的平稳过渡。

tgb34567890的博客 1039

Java SwingUtilities.invokeLater(new Runnable(){})方法详解

SwingUtilities.invokeLater(): invokeLater一般用于在线程里修改swing组件的外观,因为swing组件是非同步的,所以不能在线程中直接修改,会不同步,得不到期望的效果,所以要把修改外观的代码放在一个单独的线程中,交给invokeLater:后面的程序(线程中的重写的run()方法及run()方法中的代码)和这个参数的线程(new Runnable())对象可以并行,异步地执行 import javax.swing.SwingUtilities; public c

巴德不说话的博客 4710

人形机器人硬件开发:骨架系统设计全攻略

本文以人形机器人骨架系统为核心研究对象,系统解析其从理论建模到工程实现的全流程设计方法。通过融合刚体力学、材料科学与机器人控制理论,构建"概念基础-理论框架-架构设计-实现机制-实际应用-高级考量-综合拓展"的七层分析体系。重点覆盖运动学/动力学建模、轻量化结构设计、关节模块集成、多物理场耦合优化等关键技术,结合波士顿动力Atlas、本田ASIMO等经典案例,提供从原型设计到量产验证的工程实践指南。

AI天才研究院 1872

SwingUtilities.invokeLater

因为Swing是单线程执行的。调用invokeLater,是为了使new Runnable()和run()并行,异步执行。具体来说,将new Runnable()加入到事件处理线程中,按队列依次处理每个待处理的时间,当事件处在队首时,部件Pane立即重画,并调用run()方法执行下一个线程。

程序猿进化梯 1299

ARMv8/ARMv9的TLB深度学习

千万不要人云亦云。一个TLB entry里有很多很多东西的,有100多个比特位呢,并不是仅仅只有VA/PA/nG/VMID/ASID等。.👉👉👉 个人博客笔记导读目录(全部) 👈👈👈。

baron-周贺贺-代码改变世界ctw 1569

基于射频技术的校园无人超市管理系统_360

本设计实现了一种基于射频技术的校园无人超市管理系统,系统集成了环境监测与商品管理两大核心模块。在环境监测部分,采用DHT11温湿度传感器与MQ2烟雾传感器对超市内部环境进行实时监测,结合温控与报警机制,实现自动通风与烟雾报警功能,并通过1.44寸SPI LCD显示屏进行本地信息显示。商品管理部分基于RC522-RFID读卡器完成商品识别,结合Qt上位机软件实现顾客自助选购与结算,同时支持管理员进行商品入库与会员卡管理等操作。系统采用STM32F103RCT6为主控核心,结合串口通信方式与Qt开发的上位机进行

375

SwingUtilities.invokeLater 和SwingUtilities.invokeAndWait

SwingUtilities.invokeLater 和SwingUtilities.invokeAndWait invokeAndWait:这个程序必须等这个线程的东西执行完才能执行(存在等待)。 invokeLater:这个程序可以和线程对象可以并行,异步地执行。 一般应用于 界面上(JF)。 SwingUtilities.invokeLater : SwingU...

千寻的华尔兹的博客 430

`tokenizer.decode` 出现乱码或异常输出,怎么处理

摘要: 在使用Hugging Face Transformers库时,tokenizer.decode出现乱码或异常输出的解决方案包括:1)确保模型与分词器严格匹配,加载时使用trust_remote_code=True;2)谨慎处理特殊token,先保留所有token再针对性清理;3)校验模型输出token的维度和数值是否合法;4)通过convert_tokens_to_string替代解码;5)检查生成配置如max_new_tokens等参数。核心思路是确保模型与分词器逻辑对齐、输出token合法,必要

ZhangJiqun&191cm&85kg 584

SwingUtilities类中的invokeLater()和invokeAndWait()方法理解

      在理解SwingUtilities类中的.invokeLater()和invokeAndWait()两种方法之前,需要先了解清楚Swing线程机制。Swing程序通常包括三种类型的线程:①初始化线程(Initial Thread);②任务线程(Work Thread);③事件调度线程(Event Dispatch Thread,EDT)。      初始化线程主要负责启动程序的UI界面...

样young的博客 4735

【AI编程】Codex 进阶与高阶技巧:从熟练到精通——2026 最新教程

Skill 是 Codex 的「插件」—— 一段 Markdown 定义触发条件、输入参数和执行流程。比自定义命令更强大,支持参数化、多步骤、条件判断。

十年老程序猿,AI 疯狂研究专家,前沿硬核知识分享 192

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

SwingUtilities中invokeLater和invokeAndWait介绍

SwingUtilities中invokeLater和invokeAndWait介绍 在Java中Swing是线程不安全的,是单线程的设计,这样的造成结果就是:只能从事件派发线程访问将要在屏幕上绘制的Swing组件。事件派发线程是调用paint和update等回调方法的线程,它还是事件监听器接口中定义的事件处理方法,例如,ActionListener中的actionPerformed方法在...

我有故事你有酒? 224

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

【转】方法SwingUtilities.invokeLater()的作用

Swing是线程不安全的,也就是说只能从单线程访问Swing组件。Swing单线程设计的结果,使得大多数情况下只能从事件派发线程(事件派发线程是调用paint和update等回调方法的线程,而且还是事件监听器接口中定义的事件处理方法,如actionPerformed方法。)中访问将要在屏幕上绘制的Swing组件。     但是,有时可能需要从事件派发线程以外的线程中更新Swing组件。比如你设

hsnxyc的专栏 9304

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

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

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

SwingUtilities中invokeLater和invokeAndWait

首先看一个概念:事件派发线程(event dispath thread),它是用于swing组件的,是界面的主线程。由于Swing是线程不安全的,是单线程设计,程序中对界面的操作都必须放到时间派发线程中执行。当你在main方法中创建一个窗口类的子类像JFrame,就自动创建了一个事件派发线程,如果你在窗口中按下一个按钮或是操作其他控件,就会产生事件,响应该事件的代码就会在事件派发线程中执行。

296

SwingUtilities.invokeLater(Runnable doRun)方法的使用

 

Freesky的专栏 867
上一篇: java.lang.Process调用程序阻塞问题解决
下一篇: invokeLater和invokeAndWait
legendmoheNote
博客等级 码龄16年 12粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值