事件派发线程是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

  1. SwingUtilities.invokeAndWait(Runnable runnable)//同步请求,发送请求的线程会一直等到EDT执行完毕自己的请求后,才会继续执行剩余代码;
  2.  
  3. SwingUtilities.invokeLater(Runnable runnable)//异步请求,发送请求的线程在请求添加到EDT的eventQUEUE后,才会执行剩余代码;
  4.  
  5. SwingUtilities.isEventDispatchThread()//判断当前线程是否为事件派发线程。

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

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

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

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

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

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

  1. if(SwingUtilities.isEventDispatchThread())
  2. {
  3. OptTree.RefreshNode();
  4. }
  5. else
  6. {
  7. SwingUtilities.invokeAndWait(new Runnable()
  8. {
  9. @Override
  10. public void run()
  11. {
  12. OptTree.RefreshNode();
  13. }
  14. });
  15. }

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. JavaScript获取当前日期,昨天,今天日期以及任意天数间隔日期

    <script language="JavaScript" type="text/javascript"> function GetDateStr( ...

  2. 通过YUM库自动安装Mongodb

    1.1查看操作系统版本 1.2验证当前yum库是否包含mongodb 1.3配置包含mongodb的yum源 1.4再次验证YUM库中的mongodb 1.5 yum库安装mongodb 为方便观察, ...

  3. ThinkPHP 3.2.3 加减乘法验证码类

    ThinkPHP 3.2.3 自带的验证码类位于 /ThinkPHP/Library/Think/Verify.class.php,字体文件位于 /ThinkPHP/Library/Think/Ver ...

  4. 使用C#模拟键盘输入、鼠标移动和点击、设置光标位置及控制应用程序的显示

    1.模拟键盘输入(SendKeys) 功能:将一个或多个按键消息发送到活动窗口,就如同在键盘上进行输入一样. 语法:SendKeys.Send(string keys);SendKeys.SendWa ...

  5. macOS 10.12 解决模拟器不流畅的一段命令行

    sudo sysctl -w kern.timer.coalescing_enabled=0

  6. python学习道路(day9note)(socketserver编程,ftp)

    1.ftp client #!/usr/bin/env python #_*_coding:utf-8_*_ import socket import subprocess user_data = { ...

  7. P1220 关路灯

    #include <bits/stdc++.h> using namespace std; int d[100], w[100], s, n; int dp[100][100][3]; i ...

  8. 给Source Insight做个外挂系列之五--Insight “TabSiPlus”

    “TabSiPlus 外挂插件”主要有两部分组成,分别是“外挂插件加载器”和“插件动态库”.“插件动态库”完成Source Insight窗口的Hook,显示Tab标签栏,截获Source Insig ...

  9. ElasticSearchwindow下搭建

    ElasticSearch是一个开源的分布式搜索引擎. 下载 下载地址: https://www.elastic.co/downloads/elasticsearch 当前版本:Elasticsear ...

  10. django--一对多,多对多查询

    假设有表的关系如下: class Classes(models.Model): name = models.CharField(max_length=32) institute = models.Ch ...