java加锁多线程改为单线程_GUI为什么不设计为多线程(用户事件和底层事件的流程是相反的,每层都加锁效率太低,共用一把锁那就是单线程)...
在我們這批新人轉(zhuǎn)正評(píng)審的時(shí)候,我?guī)煾竼?wèn)了我的小伙伴一個(gè)問(wèn)題:為什么一些更新界面的方法只能在主線程中調(diào)用?師父沒(méi)有問(wèn)我這個(gè)問(wèn)題,讓知其然但不知其所以然的我有種僥幸逃過(guò)一難的心情。我想如果回答那是因?yàn)锳ndroid?GUI庫(kù)是單線程消息機(jī)制的,更新界面的操作必須放到主線程中執(zhí)行,那師父可能繼續(xù)問(wèn)為什么Android?GUI要設(shè)計(jì)成單線程的,我就不知道了。
為什么它非得設(shè)計(jì)為單線程的?多線程不是更好嗎?帶著點(diǎn)好奇感和求知欲以及鄙視權(quán)威的無(wú)畏精神我在google中展開了搜索,并最終找到了一個(gè)令我滿意的解釋,欣喜之余將我的理解分析給大家。
單線程消息隊(duì)列機(jī)制
首先我還是說(shuō)一下我對(duì)GUI單線程消息隊(duì)列機(jī)制的理解,這是我大學(xué)里幾年編程經(jīng)驗(yàn)賺來(lái)的知其然的部分。
Android、Swing、MFC等的GUI庫(kù)都使用單線程消息隊(duì)列機(jī)制來(lái)處理繪制界面、事件響應(yīng)等消息,在這種設(shè)計(jì)中,每個(gè)待處理的任務(wù)都被封裝成一個(gè)消息添加到消息隊(duì)列中。消息隊(duì)列是線程安全的(消息隊(duì)列自己通過(guò)加鎖等機(jī)制保證消息不會(huì)在多線程競(jìng)爭(zhēng)中丟失),任何線程都可以添加消息到這個(gè)隊(duì)列中,但是只有主線程(UI線程)從中取出消息,并執(zhí)行消息的響應(yīng)函數(shù),這就保證了只有主線程才去執(zhí)行這些操作。
單線程消息隊(duì)列機(jī)制存在一個(gè)問(wèn)題:消息響應(yīng)函數(shù)中不能有耗時(shí)長(zhǎng)的、計(jì)算密集型的操作,因?yàn)橹骶€程在努力地處理這樣的操作的時(shí)候就無(wú)法去處理其它的積壓在消息隊(duì)列中的繪制消息、事件消息了(一個(gè)消息處理完了主線程才會(huì)去隊(duì)列中取下一個(gè)消息),這時(shí)候就會(huì)出現(xiàn)按鍵無(wú)響應(yīng)、點(diǎn)擊無(wú)反應(yīng)的情況。
但這個(gè)問(wèn)題有完美的解決方案,我們可以在消息響應(yīng)函數(shù)中啟動(dòng)另一個(gè)工作線程(Worker?Thread)來(lái)執(zhí)行耗時(shí)操作,這樣在線程啟動(dòng)起來(lái)后這個(gè)消息就算處理完了,主線程可以取下一個(gè)消息了,這時(shí)候主線程和還未執(zhí)行完計(jì)算任務(wù)的工作線程就在操作系統(tǒng)的調(diào)度下并駕齊驅(qū)地狂奔了(調(diào)度算法會(huì)保證兩個(gè)線程并發(fā)或并行地執(zhí)行,不會(huì)專寵某個(gè)線程)。
一般我們?cè)诤臅r(shí)任務(wù)執(zhí)行完后還要更新界面展示計(jì)算的結(jié)果,由于我們不能直接在工作線程中更新界面,所以可能有些小伙伴直接在消息響應(yīng)函數(shù)中線程start后就接著調(diào)用join來(lái)等待線程結(jié)束以更新界面,這其實(shí)相當(dāng)于把耗時(shí)任務(wù)直接放在主線程去執(zhí)行,因?yàn)樵谙㈨憫?yīng)函數(shù)中join其實(shí)就是主線程在join,積壓的消息是得不到處理的。正確的處理辦法是將耗時(shí)任務(wù)改為異步通知機(jī)制,即工作線程向消息隊(duì)列中添加消息以通知主線程耗時(shí)任務(wù)完成了,這樣主線程在啟動(dòng)工作線程后就不需要主動(dòng)地去調(diào)查任務(wù)的進(jìn)展了,“任務(wù)結(jié)束的時(shí)候它會(huì)通知我的”,主線程如是說(shuō)。
工作線程向主線程的消息隊(duì)列添加消息的常用方法如下:
l?Android:Acitvity.runOnUiThead、Handler.post、AsyncTask
l?Swing:SwingUtilities.invokeLater
l?Win32、MFC:自定義用戶消息,在工作線程中PostMessage
GUI為什么不設(shè)計(jì)為多線程
大部分的GUI?toolkits都是設(shè)計(jì)為上面的單線程消息隊(duì)列機(jī)制,為什么不設(shè)計(jì)為多線程的呢?如果GUI是多線程的,CPU又是多核的話,多個(gè)CPU核心可以并行地執(zhí)行繪制等操作,界面響應(yīng)的速度應(yīng)該是成倍提升的;而且就算是其中有多線程共享的資源加鎖不就行了嗎?
在google搜索的過(guò)程中我看到了負(fù)責(zé)Swing開發(fā)的一個(gè)大師的一篇博客《Multithreaded?toolkits:?A?failed?dream?》:
從中我了解到開發(fā)多線程的GUI?toolkits是一件吃力不討好的事,不僅開發(fā)難度大Bug多多,用起來(lái)也未必可以獲得理想中的效果,其中的死鎖和競(jìng)爭(zhēng),大師們也感到頭疼。
多線程GUI加鎖困難
為什么這么困難?大師講了一個(gè)例子,我們通過(guò)用戶級(jí)的代碼去改變界面如TextView.setText走的是個(gè)自頂向下的流程:
而系統(tǒng)底層發(fā)起的如鍵盤事件、點(diǎn)擊事件走的是個(gè)自底向上的流程:
這樣就麻煩了,因?yàn)闉榱吮苊馑梨i,每個(gè)流程都要走一樣的加鎖順序,而GUI中的這兩個(gè)流程卻是完全相反的,如果每一層都有一個(gè)鎖的話加鎖就是個(gè)難以完成的任務(wù)了,而如果每一層都共用一個(gè)鎖的話,那就跟單線程沒(méi)區(qū)別了。
于是GUI?toolkits的開發(fā)者就“不負(fù)責(zé)任”地把GUI設(shè)計(jì)成了單線程消息隊(duì)列機(jī)制,然后他們還說(shuō)界面更新一般不是瓶頸,單線程足夠了。然后我瞬間想到了3D游戲,單線程對(duì)于3D應(yīng)該是很吃力的,但實(shí)際上負(fù)責(zé)3D繪制的是顯卡的GPU,GPU不像CPU那樣事無(wú)巨細(xì)、事必親躬、鞠躬盡瘁、死而后已,只負(fù)責(zé)畫好它的圖就可以了,所以并行起來(lái)不是件困難的事。
http://blog.csdn.net/liuqiaoyu080512/article/details/12895005#t1
總結(jié)
以上是生活随笔為你收集整理的java加锁多线程改为单线程_GUI为什么不设计为多线程(用户事件和底层事件的流程是相反的,每层都加锁效率太低,共用一把锁那就是单线程)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux性能分析top iostat
- 下一篇: IOS – OpenGL ES 调节图像