不同Swing線(xiàn)程的模型設(shè)計(jì)
為不同的線(xiàn)程模型進(jìn)行設(shè)計(jì)
正如前面所提到的,在搶占式模型中線(xiàn)程可以在代碼的任何一個(gè)部分的中間被打斷,除非那是一個(gè)原子操作代碼塊。原子操作代碼塊中的代碼段一旦開(kāi)始執(zhí)行,就要在該線(xiàn)程被換出處理器之前執(zhí)行完畢。在 Java 編程中,分配一個(gè)小于 32 位的變量空間是一種原子操作,而此外象 double 和 long 這兩個(gè) 64 位數(shù)據(jù)類(lèi)型的分配就不是原子的。使用鎖來(lái)正確同步共享資源的訪(fǎng)問(wèn),就足以保證一個(gè)多線(xiàn)程程序在搶占式模型下正確工作。
而在協(xié)作式模型中,是否能保證線(xiàn)程正常放棄處理器,不掠奪其他線(xiàn)程的執(zhí)行時(shí)間,則完全取決于程序員。調(diào)用 yield() 方法能夠?qū)?dāng)前的線(xiàn)程從處理器中移出到準(zhǔn)備就緒隊(duì)列中。另一個(gè)方法則是調(diào)用 sleep() 方法,使線(xiàn)程放棄處理器,并且在 sleep 方法中指定的時(shí)間間隔內(nèi)睡眠。
正如你所想的那樣,將這些方法隨意放在代碼的某個(gè)地方,并不能夠保證正常工作。如果線(xiàn)程正擁有一個(gè)鎖(因?yàn)樗谝粋€(gè)同步方法或代碼塊中),則當(dāng)它調(diào)用 yield() 時(shí)不能夠釋放這個(gè)鎖。這就意味著即使這個(gè)線(xiàn)程已經(jīng)被掛起,等待這個(gè)鎖釋放的其他線(xiàn)程依然不能繼續(xù)運(yùn)行。為了緩解這個(gè)問(wèn)題,最好不在同步方法中調(diào)用 yield 方法。將那些需要同步的代碼包在一個(gè)同步塊中,里面不含有非同步的方法,并且在這些同步代碼塊之外才調(diào)用 yield。
另外一個(gè)解決方法則是調(diào)用 wait() 方法,使處理器放棄它當(dāng)前擁有的對(duì)象的鎖。如果對(duì)象在方法級(jí)別上使同步的,這種方法能夠很好的工作。因?yàn)樗鼉H僅使用了一個(gè)鎖。如果它使用 fine-grained 鎖,則 wait() 將無(wú)法放棄這些鎖。此外,一個(gè)因?yàn)檎{(diào)用 wait() 方法而阻塞的線(xiàn)程,只有當(dāng)其他線(xiàn)程調(diào)用 notifyAll() 時(shí)才會(huì)被喚醒。
AWT/Swing線(xiàn)程
在那些使用 Swing和AWT 包創(chuàng)建 GUI (用戶(hù)圖形界面)的 Java 程序中,AWT 事件句柄在它自己的線(xiàn)程中運(yùn)行。開(kāi)發(fā)員必須注意避免將這些 GUI 線(xiàn)程與較耗時(shí)間的計(jì)算工作綁在一起,因?yàn)檫@些線(xiàn)程必須負(fù)責(zé)處理用戶(hù)時(shí)間并重繪用戶(hù)圖形界面。換句話(huà)來(lái)說(shuō),一旦 GUI 線(xiàn)程處于繁忙,整個(gè)程序看起來(lái)就象無(wú)響應(yīng)狀態(tài)。Swing線(xiàn)程通過(guò)調(diào)用合適方法,通知那些 Swing callback (例如 Mouse Listener 和 Action Listener )。 這種方法意味著 listener 無(wú)論要做多少事情,都應(yīng)當(dāng)利用 listener callback 方法產(chǎn)生其他線(xiàn)程來(lái)完成此項(xiàng)工作。目的便在于讓 listener callback 更快速返回,從而允許 Swing線(xiàn)程響應(yīng)其他事件。
如果一個(gè) Swing線(xiàn)程不能夠同步運(yùn)行、響應(yīng)事件并重繪輸出,那怎么能夠讓其他的線(xiàn)程安全地修改 Swing 的狀態(tài)?正如上面提到的,Swing callback 在 Swing線(xiàn)程中運(yùn)行。因此他們能修改 Swing 數(shù)據(jù)并繪到屏幕上。
但是如果不是 Swing callback 產(chǎn)生的變化該怎么辦呢?使用一個(gè)非 Swing線(xiàn)程來(lái)修改 Swing 數(shù)據(jù)是不安全的。Swing 提供了兩個(gè)方法來(lái)解決這個(gè)問(wèn)題:invokeLater() 和 invokeAndWait()。為了修改 Swing 狀態(tài),只要簡(jiǎn)單地調(diào)用其中一個(gè)方法,讓 Runnable 的對(duì)象來(lái)做這些工作。因?yàn)?Runnable 對(duì)象通常就是它們自身的線(xiàn)程,你可能會(huì)認(rèn)為這些對(duì)象會(huì)作為線(xiàn)程來(lái)執(zhí)行。但那樣做其實(shí)也是不安全的。事實(shí)上,Swing 會(huì)將這些對(duì)象放到隊(duì)列中,并在將來(lái)某個(gè)時(shí)刻執(zhí)行它的 run 方法。這樣才能夠安全修改 Swing 狀態(tài)。
【編輯推薦】