事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点。今天我们在多线程开发中,穿插进来这个线程。分别从线程的来由、原理和使用方法三个方面来学习事件派发线程。

一、事件派发线程的前世今生

事件(Event)派发(Dispatch)线程(Thread)简写为EDT,也就是各个首字母的简写。在一些书或者博客里边也将其译为事件分发线程、事件调度线程。巴拉巴拉,总之,知道这些名字就行。笔者认为这里翻译成派发更准确点。

熟悉Swing和awt编程的小伙伴对事件派发线程应该都不陌生。如果你提反对意见的话,只能说明你对Swing和awt编程还不够熟悉。

事件派发线程诞生的故事背景是这样的:

界面上各个控件对象都有保存自己的数据变量。如果出现多线程操作就会出现很多问题,诸如数据变脏,数组越界,空引用等等问题。

举个栗(例)子

线程A发现panel中还有数据要显示(check data),于是调用滚动条向下滚动。这时,panel内部要调用数据中为展示的数据用来显示。可是在展示的过程中,线程发生了切换。由其它线程B删掉了需要展示的数据,这时线程A再次被唤醒继续运行,显示接下来的内容。由于已经过了Check Data的逻辑。所以接下来就要调用已经不存在的数据用来展示。最后就会出现各种奇怪的问题。(如果你没看懂,就理解成各个线程最终都在操作控件的数据源,则控件在显示的时候就可能会出现异常)。

通常来说解决这种多线程冰法问题方式就是"锁"或者"同步"。

当时Sun公司的Swing小组最初也是这个思路,但是让Swing小组最终改变主意的由于接下来的两个原因:

1、数据同步在保证线程安全的同时,很耗费时间。UI最重要的就是界面响应速度,毕竟谁也不想面对一个幻灯片在操作。

2、Swing小组调查了其他小组在线程安全的用户界面工具包方(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )面的经验后,发现结果并不是那么的美好:开发线程安全包的工程师被各种同步操作搞晕了头,程序经常发生死锁。

就此,Swing小组决定使用单一线程来控制整个界面的控件绘制。这个线程就是事件派发线程。

事件派发线程就是这样被创造出来的:

二、事件派发线程的原理

事件派发线程的原理其实非常的简单,在界面后台始终只有这一个线程在工作,这个线程就是事件派发线程。当你有需要操作界面的行为时,将这些行为添加到事件派发线程的事件队列中,事件派发线程会依次执行这个队列中的请求。

这有点像单核cpu进行多线程操作的场景,不同的地方是,这时候事件派发线程的作用是单核cpu。

具体内容可以查看下图(图片来源于网络)

各个线程将GUI请求排成队列,然后由事件派发线程依次执行这个队列中的请求。

如果从设计模式的角度来看,这个地方是一个典型的"消费者"模式,有兴趣的小伙伴可以查阅下相关的设计模式内容,这里就不展开赘述了。

了解了事件派发线程原理之后,我们会发现这样一个问题:

eventQueue中的事件没有轻重缓急之分,是遵循FIFO的原则的。那么如果前边的请求非常耗时,需要大量的db请求、IO等操作,那么后边的请求只能一直等待。

当初舍弃'同步'是为了快,现在界面还是会卡死,违背了初衷。

基于以上,Swing开发人员提出了两点在使用事件派发线程时需要遵守的原则:

1、只有事件派发线程可以调用Swing组件,其他线程都离组件远远的。(有些地方称这条准则为单一线程规则single-thread rule)

2、如果某一个GUI请求非常耗时,就不要把这个请求发送给事件派发线程。直到这个请求通过其他线程处理之后,最后的少部分界面请求再发送给事件派发线程。

三、怎么使用事件派发线程

上面说了非常多,但是不知道怎么使用事件派发线程,则上边所述也就没有什么用了。

首先,前文中提到了事件派发线程是启动GUI后,(其实这里还存在有一个初始化线程,短暂的启动GUI的生命过程)系统自动启动的一个线程。

所以我们就不用手动创建和运行线程了。我们要做的就(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )是向事件派发线程中添加各种GUI请求到eventQUEUE中去即可。

Swing为我们提供了三个常用的API

 SwingUtilities.invokeAndWait(Runnable runnable)//同步请求,发送请求的线程会一直等到EDT执行完毕自己的请求后,才会继续执行剩余代码;

 SwingUtilities.invokeLater(Runnable runnable)//异步请求,发送请求的线程在请求添加到EDT的eventQUEUE后,才会执行剩余代码;

 SwingUtilities.isEventDispatchThread()//判断当前线程是否为事件派发线程。

一般来说在编写请求代码的时候,最好先判断下执行线程是否为事件派发线程,然后在选择是直接执行还是添加到事件队列中。

值得注意的是这里会存在一个问题:

就是如果当前线程就是事件派发线程时,是不允许其执行invokeAndWait()同步方法的。

这是由于如果出现这种情况EDT就会停顿(wait)在这个点,等待EDT去执行添加的请求,同时由于EDT已经停顿在了这个点,那么EDT也就不会去处理eventQUEUE中的请求,形成了一种死锁。

好在JDK中已经对这种情况做了校验,所以上面没太看懂的同学无需太在意,只要记住结果即可:

最后我们再来一个实际工作中代码的例子

 if(SwingUtilities.isEventDispatchThread())
{
OptTree.RefreshNode();
}
else
{
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run()
{
OptTree.RefreshNode();
}
});
}

Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread的更多相关文章

  1. 知识图谱实战开发案例剖析-番外篇(1)- Neo4j是否支持按照边权重加粗和大数量展示

    一.前言 本文是<知识图谱实战开发案例完全剖析>系列文章和网易云视频课程的番外篇,主要记录学员在知识图谱等相关内容的学习 过程中,提出的共性问题进行展开讨论.该部分内容原始内容记录在网易云 ...

  2. Java多线程开发系列之一:走进多线程

    对编程语言的基础知识:分支.选择.循环.面向对象等基本概念理解后,我们需要对java高级编程有一定的学习,这里不可避免的要接触到多线程开发. 由于多线程开发整体的系统比较大,我会写一个系列的文章总结介 ...

  3. Java多线程开发系列之四:玩转多线程(线程的控制2)

    在上节的线程控制(详情点击这里)中,我们讲解了线程的等待join().守护线程.本节我们将会把剩下的线程控制内容一并讲完,主要内容有线程的睡眠.让步.优先级.挂起和恢复.停止等. 废话不多说,我们直接 ...

  4. Java多线程开发系列之四:玩转多线程(线程的控制1)

    在前文中我们已经学习了:线程的基本情况.如何创建多线程.线程的生命周期.利用已有知识我们已经可以写出如何利用多线程处理大量任务这样简单的程序.但是当应用场景复杂时,我们还需要从管理控制入手,更好的操纵 ...

  5. Java多线程开发系列之三:线程这一辈子(线程的生命周期)

    前文中已经提到了,关于多线程的基础知识和多线程的创建.但是如果想要很好的管理多线程,一定要对线程的生命周期有一个整体概念.本节即对线程的一生进行介绍,让大家对线程的各个时段的状态有一定了解. 线程的一 ...

  6. Java多线程开发系列之二:如何创建多线程

    前文已介绍过多线程的基本知识了,比如什么是多线程,什么又是进程,为什么要使用多线程等等. 在了解了软件开发中使用多线程的基本常识后,我们今天来聊聊如何简单的使用多线程. 在Java中创建多线程的方式有 ...

  7. Java多线程开发系列之五:Springboot 中异步请求方法的使用

    Springboot 中异步线程的使用在过往的后台开发中,我们往往使用java自带的线程或线程池,来进行异步的调用.这对于效果来说没什么,甚至可以让开发人员对底层的状况更清晰,但是对于代码的易读性和可 ...

  8. 粮草先行——Android折叠屏开发技术点番外篇之运行时变更处理原则

    上一篇文章中,我们有提到Activity在屏幕尺寸发生变更时的处理方式,总共有两种: 重启APP以适应屏幕改变: 手动处理数据,避免APP重启. 同样,这两种方式也同时适用于改变屏幕方向.更改系统语言 ...

  9. 【C# 开发技巧】番外篇故事-我是一个线程

    我是一个线程 我是一个线程,一出生就被编了一个号——0x3704,然后被领到一间昏暗的屋子里,在这里,我发现了很多和我一模一样的同伴.我身边的同伴0x6900待的时间比较长,他带着沧桑的口气对我说:“ ...

随机推荐

  1. 【系统篇】从C/C++语言到进程启动背后的故事

    我们需要运行一个程序或者软件,双击之即可完成.不过从你双击到程序的窗口产生的这“短暂”的时间内,Windows为你做了很多的工作. 首先,系统有一个进程监测到了你的双击操作,这个进程就是系统shell ...

  2. .NET 二维码生成(ThoughtWorks.QRCode)

    引用ThoughtWorks.QRCode.dll (源代码里有) 1.简单二维码生成及解码代码: //生成二维码方法一 private void CreateCode_Simple(string n ...

  3. EasyUI配置和组件

    首先在webcontent添加配置文件 新建静态或动态网站,在title的下面加入五个配置文件路径,注意:循序不能乱 <!-- 顺序不可以乱 --> <!-- 1.jQuery的js ...

  4. 6个函数的output看JS的块级作用域

    1. var output = 0; (function() { output++; }()); console.log(output); 函数对全局的output进行操作,因为JS没有块级作用域,所 ...

  5. 谢欣伦 - OpenDev原创教程 - 本地IP查找类CxLocalHostIPAddrFind

    这是一个精练的本地IP查找类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxLocalHostIPAddrFind的使用如下: ...

  6. AD Local Domain groups, Global groups and Universal groups

    http://ss64.com/nt/syntax-groups.html Rules that govern when a group can be added to another group ( ...

  7. Symmetric Multiprocessor Organization

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION

  8. event事件对象

    事件对象event: 在触发DOM事件的时候都会产生一个对象 1.type:获取事件类型 2.target:获取事件目标 3.stopPropagation():组织事件冒泡 4.preventDef ...

  9. 用TPP开启TDD的easy模式

    Test-Drived Development 测试驱动开发三步曲:写一个失败的测试用例->编写生产代码通过这个测试用例(transformation)->重构(refactor).重构是 ...

  10. 微信小程序-WXSS

    WXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式. WXSS 用来决定 WXML 的组件应该怎么显示. 为了适应广大的前端开发者,我们的 WXSS 具有 ...