国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java多線程開發系列之番外篇:事件派發線程---EventDispatchThread

2019-11-14 15:01:06
字體:
來源:轉載
供稿:網友

事件派發線程是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://m.survivalescaperooms.com/jilodream/ )面的經驗后,發現結果并不是那么的美好:開發線程安全包的工程師被各種同步操作搞暈了頭,程序經常發生死鎖。

就此,Swing小組決定使用單一線程來控制整個界面的控件繪制。這個線程就是事件派發線程。

事件派發線程就是這樣被創造出來的:

 

二、事件派發線程的原理

事件派發線程的原理其實非常的簡單,在界面后臺始終只有這一個線程在工作,這個線程就是事件派發線程。當你有需要操作界面的行為時,將這些行為添加到事件派發線程的事件隊列中,事件派發線程會依次執行這個隊列中的請求。

這有點像單核cpu進行多線程操作的場景,不同的地方是,這時候事件派發線程的作用是單核cpu。

具體內容可以查看下圖(圖片來源于網絡)

 

各個線程將GUI請求排成隊列,然后由事件派發線程依次執行這個隊列中的請求。

如果從設計模式的角度來看,這個地方是一個典型的"消費者"模式,有興趣的小伙伴可以查閱下相關的設計模式內容,這里就不展開贅述了。

了解了事件派發線程原理之后,我們會發現這樣一個問題:

eventQueue中的事件沒有輕重緩急之分,是遵循FIFO的原則的。那么如果前邊的請求非常耗時,需要大量的db請求、IO等操作,那么后邊的請求只能一直等待。

當初舍棄'同步'是為了快,現在界面還是會卡死,違背了初衷。

基于以上,Swing開發人員提出了兩點在使用事件派發線程時需要遵守的原則:

1、只有事件派發線程可以調用Swing組件,其他線程都離組件遠遠的。(有些地方稱這條準則為單一線程規則single-thread rule)

2、如果某一個GUI請求非常耗時,就不要把這個請求發送給事件派發線程。直到這個請求通過其他線程處理之后,最后的少部分界面請求再發送給事件派發線程。

 

三、怎么使用事件派發線程

上面說了非常多,但是不知道怎么使用事件派發線程,則上邊所述也就沒有什么用了。

首先,前文中提到了事件派發線程是啟動GUI后,(其實這里還存在有一個初始化線程,短暫的啟動GUI的生命過程)系統自動啟動的一個線程。

所以我們就不用手動創建和運行線程了。我們要做的就(防盜連接:本文首發自http://m.survivalescaperooms.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         @Override10         public void run()11         {12             OptTree.RefreshNode();13         }14     });15 }


 

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 稻城县| 集贤县| 汕尾市| 巴林右旗| 广汉市| 锡林浩特市| 朝阳市| 泸溪县| 华坪县| 景东| 保亭| 山丹县| 西畴县| 大埔县| 平陆县| 托克托县| 东光县| 宜君县| 连平县| 新民市| 仪征市| 南宁市| 水富县| 布拖县| 杨浦区| 盱眙县| 闽清县| 皋兰县| 福泉市| 龙口市| 呼玛县| 普兰店市| 舟曲县| 游戏| 喀喇| 八宿县| 大田县| 探索| 章丘市| 重庆市| 收藏|