Java Swing 常识篇 之 EDT


 Java Swing 常识篇之EDT

       从毕业到现在用SWING已经一年多,在这里想总结一下过去学到的东西和经验,和各位兄弟姐妹们一起分享。在以后的文章中也会和大家一起来分享一些好的框架。说起JAVA SWING,普遍给人的感觉是“丑、慢、难”,丑是界面丑、慢是速度慢、难是开发难。其实熟悉LAF的同学都知道SWING确实不丑;说到‘慢’,SWING也不慢,只要懂得如何处理长时间的TASK,而不是什么都在EDT处理;说到难,只要我们有一套自己专门开发SWINGUI的类库,也不难了。所以在接下来的文章中,我将会和大家构建一个SWING UI FRAMEWORK(暂时称为:Fast-DUI),在这个Fast-DUI中将会整合SWING中的动画(淡入淡出、滑上滑下)、效果(shadow、drop-shadow)、感官(LookAndFeel)等等。构建这样的一个框架主要是为了能够快速地开发出“漂亮的、用户体验好的”桌面应用程序。好了,接下来马上开始。今天的这篇文章在Swing的应用中起到举足轻重的作用。这篇文章是开头篇,可能写得不好,感觉自己的写作水平,和小学生有得一比。
        今天就一起来学习一下EDT(Event Dispatching Thread,字面上翻译成“事件分配线程”),那什么是EDT,EDT就简单地认为就是一个线程(特殊的线程),这个线程主要是管理着整个SWING GUI的事件,管理着整个UI界面(熟悉Java2D的朋友都知道,我们所看到的界面就是Java2D绘图绘出来的)。大家都知道,也许大学的时候,老师也会提到SWING不是线程安全的,不是线程安全的,那意味着我们用多线程的时候,要格外的小心,我们要把SWING里面东西的修改、获取独立放到一个线程上,但是这个刚好SWING的开发人员已经为我们解决了,我们不必自己管理一个额外的线程,我们只需要把他们放到EDT里面,下面我会举两个例子来说明一下如何使用EDT。
          第一个例子来讲一下如果不在EDT中修改Swing的东西会出现什么问题,请看下面的例子:

pakage org.dui.sample

import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

/**
 * <code>NotInEDTSample</code> just demonstrates the usage of Swing EDT simply.
 * 
 * @author Jimmy.haung(SZ Team)
 * @since <i>DUI (Mar 25, 2013)</i>
 */
public class NotInEDTSample extends JFrame {
	private static final long serialVersionUID = 1L;
	private JTextField m_txt;

	public NotInEDTSample() {
		initGUI();
		notInEDT();
	}

	/**
	 * Init the GUI
	 */
	public void initGUI() {
		this.setTitle("a simple EDT Sample");
		m_txt = new JTextField();
		getContentPane().add(m_txt, BorderLayout.CENTER);
	}

	/**
	 * Process not under the EDT. 这里我启动了10个线程来改变<code>m_txt</code>的内容.
	 */
	private void notInEDT() {
		for (int i = 0; i < 4; ++i) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					while (true) {
						m_txt.setText("我不在EDT中操作!");
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}).start();
		}
	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
					NotInEDTSample oFrame = new NotInEDTSample();
					oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
					oFrame.setLocationRelativeTo(null);
					oFrame.setSize(300, 200);
					oFrame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

}

     运行上面的程序,然后点击几下界面后,界面就‘死掉’了(大家可以Resize看一下,或者点击一下窗口的Close按钮),‘死掉’有可能是死锁造成的,只是可能,但是程序也没有报Exception,这就是不确定性,如果Swing的stub不在EDT里面运行,带给程序很多的不确定性。在我以前看到的项目中,由于EDT的原因,引发可各种不可预测的Exception。

如果我们将上面的程序稍微改一下,将notInEDT的方法改成下面的样子:

private void notInEDT() {
	for (int i = 0; i < 4; ++i) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					SwingUtilities.invokeLater(new Runnable() {
						@Override
						public void run() {
						     m_txt.setText("我在EDT中操作!");
						}
					});
					
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
	}
}

      通过修改是不是发现没前面的问题了。但是有一些新学的朋友会问,如果本来就在EDT中就不用再加入到EDT, 但是如何才知道当前的线程是否是EDT呢,请看SwingUtilities.isEventDispatchThread();想得多周到的开发Swing的人员!SwingUtilities、SwingUtilities2、SwingUtilities3这几个类在以后都会经常用到,非常方便。看了上面的例子,大家有什么感想呢?

       下面我们接着来写一个例子,我们试想一下,如果在EDT中执行一些时间长的任务会怎样呢?

 

import java.awt.BorderLayout;

/**
 * <code>NotInEDTSample</code> just demonstrates the usage of Swing EDT simply.
 * 
 * @author Jimmy.haung(SZ Team)
 * @since <i>DUI (Mar 25, 2013)</i>
 */
public class LongTaskSample extends JFrame {
	private static final long serialVersionUID = 1L;
	private JButton m_btn;

	public LongTaskSample() {
		initGUI();
		initEventHandler();
	}

	/**
	 * Init the GUI
	 */
	public void initGUI() {
		this.setTitle("a simple EDT Sample");
		m_btn = new JButton("click me to process long task");
		getContentPane().add(m_btn, BorderLayout.CENTER);
	}
	/**
	 * Bind the event to the component.
	 */
	public void initEventHandler(){
		m_btn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				longTask();
			}
		});
	}

	/**
	 * Use about five seconds to process this task.
	 */
	private void longTask() {
		//为true,请看 #main(String[])
		System.err.println(SwingUtilities.isEventDispatchThread());
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
					LongTaskSample oFrame = new LongTaskSample();
					oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
					oFrame.setLocationRelativeTo(null);
					oFrame.setSize(300, 200);
					oFrame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

}

        

       点击了按钮后,我们发现程序死掉了大约5秒后,就醒来了,这就是“装死”的现象。所以我们看到在EDT里面运行时间长的任务的结果会引发什么后果,就会觉得很慢。上面的两个例子只是说明了一丁点的问题。在大家以后的项目中将会遇到更多问题。那么现在的问题是如果来改善上面的问题,我们应该在其他的线程中执行时间长的任务,而在EDT只是负责界面的显示,以及界面数据的修改、获取等,这以后还会讲,在这里提示一下,SwingWorker可以很好解决上面的问题。马马虎虎终于完成了第一篇文章。如果新学的朋友,还是不懂得话,请看http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html。如果还是不明白,不要紧,以后会慢慢体会到的。前面的几篇的文章将会将一些Swing基本的东西,讲完基础的东西后将会开始我们的Fast-DUI之旅。预告一下,下一章将会是布局(Layout),如何简单使用布局管理器来实现复杂的布局。

   这里先预告一下我们以后要构建Fast-DUI的LAF(这只是其中的一种Skin,大家可以根据自己的情况来改变Skin),如下图:


Java Swing 常识篇 之 EDT
 

你可能感兴趣的:(swing,EDT,Swing常识,事件分配线程)