从swing分发线程机制上理解多线程

    本文参考了 http://space.itpub.net/13685345/viewspace-374940,原文作者:javagui
    在多线程编程当中,总会提到图形编程,比如java中的swing,并一再提出,在swing中,一切都是单线程的。所有的界面更新操作都必须在排队似地进行。这样的目的在于,避免由于多线程的处理导致界面渲染以及组件排列异常,同时也避免了由于多线程带来的加锁访问以及等待锁的情况发生。

    EventQueue的派发机制由单独的一个线程管理,这个线程称为事件派发线程(EDT)”。和其他很多桌面API一样,Swing将GUI请求放入一个事件队列中执行。
    这个可以这样来理解,界面上所有的操作,包括点击按钮,编辑文字等。这些操作都会产生一系列的事件,而这些事件均按钮一定的顺序(通常是事件发生的顺序)依次的插入到事件队列中。而分线线程则按照顺序从事件队列中依次一个一个地取出事件对象,并调用事件的相应方法来运行处理事件的方法。
    在整个处理阶段,分发线程必须要等到一个事件处理方法运行结束之后,才会处理下一个事件。在这个过程中,肯定有些事件处理得快,比如普通的点击按钮改变颜色的这种操作;而有些事件则会非常的慢,比如点击按钮运行一个长时间的数据压缩。我们希望,处理得慢的事件在处理时不致于影响其它事件的处理。

    这就需要使用其它线程来处理这些耗时地任务,比如使用一个新的线程来处理这些耗时任务。这样可以处理处理事件的程序快速地返回,而不至于影响其它事件的执行。但是在处理过程中,我们必须保证处理的代码不能影响到界面的变化。主要依赖于以下的原则:

    EDT要处理所有GUI操作,它是职责分明且非常忙碌的。也就是说你要记住两条原则:一、职责分明,任何GUI请求都应该在EDT中调用。二、需要处理的GUI请求非常多,包括窗口移动、组件自动重绘、刷新,它很忙,所以任何与GUI无关的处理不要由EDT来负责,尤其是I/O这种耗时的操作。

    那么,如果我们要在我们新建的线程中,要调用界面处理程序应该如何去做呢。这时可以使用SwingUtilities工具类的invokeLater或者invokeAndWait方法来执行界面更新程序。这两个方法,是将我们传递的一个线程方法放入到事件分发线程当中,这样可保证所有更新界面的程序都能够在事件处理线程中运行。
    由于invokeLater和invokAndWait方法是在事件分发线程中运行的,所以这两个方法不能在事件分发线程中调用,特别是invokeAndWait方法,这会导致线程死锁。所幸的是,jdk的invokeAndWait实现避免了这种情况,如果在事件处理线程中调用invokeAndWait,那会产生一个异常。

    说到底,什么是事件处理线程呢,什么才算在事件处理线程当中呢。很简单的理解,我们添加到各个对象上的listener实现中的处理代码,都是在事件处理线程中运行的。比如按钮的点击事件处理程序,actionPerformed方法,这都是在事件处理线程中调用的。

    通过上述对事件队列和EDT的分析,有这样一种体会:事件队列是一个非常好的处理并发设计模型,不仅 Swing用它来处理后台,Java的很多地方都在用,只不过对于处理服务器端的并发请求有多个处理线程在等候处理请求,也就是常说的线程池。而对于单用户的桌面应用,单线程调用要比多现成API更简单,“Swing后台这样做是为了保证事件的顺序和可预见性”,而且相对于服务器,客户端桌面层的请求要少得多,所以单线程就足够应对了。

    那么,swing的分发线程机制和多线程之间有什么联系呢?swing中的线程处理原则在于,界面处理程序在事件处理线程中运行,而其它逻辑运算在其它线程中运行,通过invokeLator和invokeAndWait和事件处理线程交互。而我们通过使用的多线程,在一定程度上也是这样处理。主要的程序处理在主线程中运行,当接收到处理请求时,则分发线程进行,即要保证主线程不会因为事件处理而被阻塞,同时也要保证事件都能够被处理。比如经常使用的web开发中的socket请求。

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201108110001.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

电子邮件地址不会被公开。 必填项已用*标注