Java线程—如何解决Swing的单线程问题-----------Swing线程机制

时间:2023-01-28 15:43:30

首先swing是单线程的,这个是这篇文章的前提,也是意义所在,当swing界面程序启动的时候,会启动3个进程,

1、主线程

2、系统工具包线程:负责捕获操作系统事件,然后将事件转换成swing的事件,然后发送到事件派发线程EDT

3、事件派发线程(EDT):将事件派发到各个组件,并负责调用绘制方法更新界面


所有的事件,例如键盘,鼠标事件,都会由工具包线程转换成swing事件,然后放到事件队列EventQueue中,而这个EventQueue的派发机制是由EDT来管理的。

EDT管理队列

Java线程—如何解决Swing的单线程问题-----------Swing线程机制

所以任何修改组件状态的方法都应该在EDT中执行,包括构造方法。Swing这样的构造原理经常会造成的情况就是,在EDT中执行长时间的事件,使EDT不能及时响应更新界面的事件,就是所说的界面卡住,这种不光是新手就是比较熟练的程序员也会犯的一个错误。所以必须避免在EDT中执行长时间的操作,而避免的方法就是多线程,启动另外的线程来处理冗长的操作,比如操作数据库,读写文件等,在这过程中可能要更新界面来给用户以提示,比如显示一个进度条,过一段时间更新一下界面,但是在EDT以外的线程中更新界面都是无效的,这在前面已经说过,要更新界面就要将对界面的更新操作放到EDT中,但是事件又是在另外的线程中执行的,要解决这个问题就要使用SwingUtilities提供的两个静态方法了 invokeLater,invokeAndWait

举例:

invokeLater的使用

class GetInfoThread extends Thread {

  Test applet;
  Runnable runx;
  int value;

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

   publicvoid run() {
    while (true) {
     try {
      Thread.sleep(500);
      value = (int) (Math.random() * 100);
      System.out.println(value);
      SwingUtilities.invokeLater(runx);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }
   }
  }
 

《》invokeAndWait和invokeLater的区别

与invoikeLater一样,invokeAndWait也把可运行对象排入事件派发线程的队列中,invokeLater在把可运行的对象放入队列后就返回,而invokeAndWait一直等待直到提交的run方法执行完毕后才返回。如果操作2,必须得等到操作1完毕之后,才能够执行的话,则invokeAndWait方法是很有用的,那么我们就用invokeAndWait将操作1提交,之后再执行操作2就行了;

invokeAndWait的使用

class GetInfoThread extends Thread {
   
   Runnable getValue,setValue;
   int value,currentValue;

   public GetInfoThread(final Test applet){

   getValue=new Runnable(){
   publicvoid run(){
    JProgressBar pb=applet.getProgressBar();
    currentValue=pb.getValue();
    }
   };

   setValue=new Runnable(){
    publicvoid run(){
     JProgressBar pb=applet.getProgressBar();
     pb.setValue(value);
    }
   }
   }

   publicvoid 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){
      }
    }
   }
 

注意:这两个方法的作用只是将一个更新界面的Runnable任务提交给EDT线程,EDT会在适当的时候进行调用以更新界面。