Swing与线程

    无论何时运行一个Swing应用程序,都会自动创建三个线程。第一个是主线程,它运行程序的主方法。第二个线程称为工具包线程,它负责捕获系统事件,如键盘按下或鼠标移动。捕获的事件将发送到第三个线程(事件派发进程)。
    所有的事件(如键盘按下、鼠标移动)都放置到一个事件队列中。Java.awt.EventQueue对象负责从这个队列中取出事件并适当地派发它们。EventQueue的派发机制由单独的一个线程管理,这个线程称为事件派发线程(EDT,Event Dispatch Thread)。
    比如在JTextField按下一个键,EDT把这个按键事件派发到这个组件的键监听器。这个组件接着更新他的模式(执行actionPerformed方法),并把一个绘制请求发布到事件队列(EventQueue)。EDT从这个队列中取出这个绘制请求并再次通知这个组件,让它重新绘制自身。

    运用Swing有两个要注意的地方:
    一是Swing不是线程安全的,只能在事件派发线程上访问Swing组件。多线程操控组件可能导致死锁。以前把Swing写在主线程方法是不正确的,应该借用SwingUtilities的invokeLater或invokeAndWait方法,其他线程通过这两个方法可以将代码放到事件队列中。其中invokeLater()方法是异步的,即一旦事件被发往事件队列,invokeLater的调用会立即返回。而invokeAndWait()阻塞当前进程并等待,直到EDT执行该事件。
    正确的写法如下:
public static void main(String[] args){
	SwingUtilities.invokeLater(new Runnable(){
		public void run() {
			//.......
		}
	});
}


    第二个要注意的地方是不要让EDT执行耗时较长的操作,例如读写文件,将阻塞整个UI。在运行这个耗时较长的操作时不能派发事件,也不能执行屏幕更新。
public class FreezeEDT extends JFrame implements ActionListener {
  private JButton freezer;
  public FreezeEDT() {
    super("Freeze");
    freezer = new JButton("Freeze");
    freezer.addActionListener(this);
    add(freezer);
    pack();
  }

  public void actionPerformed(ActionEvent e) {
    try {
      Thread.sleep(4000);
    } catch (InterruptedException evt) {
    }
  }

  public static void main(String... args) {
    SwingUtilities.invokeLater(new Runnable(){
		public void run() {
		    FreezeEDT edt = new FreezeEDT();
		    edt.setVisible(true);
		}
	});
  }
}

这里让EDT线程休眠了四秒,按钮看起来像冻结了一样。为了避免执行耗时操作,一般是创建另外一个线程,但前面已经说过,只能在EDT线程访问Swing组件,所以在创建新线程的同时仍要将新线程里访问Swing组件的代码交给invokeLater方法。
改进代码如下:
public class FreezeEDT extends JFrame implements ActionListener {
  private JButton freezer;
  public FreezeEDT() {
    super("Freeze");
    freezer = new JButton("Freeze");
    freezer.addActionListener(this);
    add(freezer);
    pack();
  }

  public void actionPerformed(ActionEvent e) {
	  new Thread(new Runnable(){
		public void run() {
			try {
				Thread.sleep(4000);
			} catch (InterruptedException evt) {
			}
                           final String hot = "Hot";
			SwingUtilities.invokeLater(new Runnable(){
				public void run() {
					freezer.setText(hot);
				}
			});
		}
	  }).start();
  }

  public static void main(String... args) {
    SwingUtilities.invokeLater(new Runnable(){
		public void run() {
		    FreezeEDT edt = new FreezeEDT();
		    edt.setVisible(true);
		}
	});
  }
}


注意:
invokeLater并不会产生新线程,其Runnable对象会被包装成一个事件,并添加到事件队列中,处理事件时只会执行run方法。

参考文献:《Java动画、图形和极富客户端效果开发》,Chet Haase&Romain Guy

你可能感兴趣的:(java,thread,多线程,swing)