




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、編寫多線程的 Java 應用程序如何避免當前編程中最常見的問題Alex Roetter (aroetterCS.STeton Data Systems 的軟件工程師2001 年 2 月Java Thread API 允許程序員編寫具有多處理機制優(yōu)點的應用程序在后臺處理任務的同時保持用戶所需的交互感Alex Roetter 介紹了Java Thread API并概述多線程可能引起的問題以及常見問題的解決方案幾乎所有使用 AWT 或 Swing 編寫的畫圖程序都需要多線程但多線程程序會造成許多困難剛開始編程的開發(fā)者常常會發(fā)現(xiàn)他們被一些問題所折磨例如不正確的程序行為或死鎖在本文
2、中我們將探討使用多線程時遇到的問題并提出那些常見陷阱的解決方案線程是什么 一個程序或進程能夠包含多個線程這些線程可以根據(jù)程序的代碼執(zhí)行相應的指令多線程看上去似乎在并行執(zhí)行它們各自的工作就像在一臺計算機上運行著多個處理機一樣在多處理機計算機上實現(xiàn)多線程時它們確實可以并行工作和進程不同的是線程共享地址空間也就是說多個線程能夠讀寫相同的變量或數(shù)據(jù)結(jié)構(gòu)編寫多線程程序時你必須注意每個線程是否干擾了其他線程的工作可以將程序看作一個辦公室如果不需要共享辦公室資源或與其他人交流所有職員就會獨立并行地工作某個職員若要和其他人交談當且僅當該職員在聽且他們兩說同樣的語言此外只有在復印機空閑且處于可用狀態(tài)沒有僅完成一
3、半的復印工作沒有紙張阻塞等問題時職員才能夠使用它在這篇文章中你將看到在 Java 程序中互相協(xié)作的線程就好像是在一個組織良好的機構(gòu)中工作的職員在多線程程序中線程可以從準備就緒隊列中得到并在可獲得的系統(tǒng) CPU 上運行操作系統(tǒng)可以將線程從處理器移到準備就緒隊列或阻塞隊列中這種情況可以認為是處理器掛起了該線程同樣Java 虛擬機 (JVM 也可以控制線程的移動在協(xié)作或搶先模型中從準備就緒隊列中將進程移到處理器中于是該線程就可以開始執(zhí)行它的程序代碼協(xié)作式線程模型允許線程自己決定什么時候放棄處理器來等待其他的線程程序開發(fā)員可以精確地決定某個線程何時會被其他線程掛起允許它們與對方有效地合作缺點在于某些惡
4、意或是寫得不好的線程會消耗所有可獲得的 CPU 時間導致其他線程饑餓 在搶占式線程模型中操作系統(tǒng)可以在任何時候打斷線程通常會在它運行了一段時間就是所謂的一個時間片后才打斷它這樣的結(jié)果自然是沒有線程能夠不公平地長時間霸占處理器然而隨時可能打斷線程就會給程序開發(fā)員帶來其他麻煩同樣使用辦公室的例子假設(shè)某個職員搶在另一人前使用復印機但打印工作在未完成的時候離開了另一人接著使用復印機時該復印機上可能就還有先前那名職員留下來的資料搶占式線程模型要求線程正確共享資源協(xié)作式模型卻要求線程共享執(zhí)行時間由于 JVM 規(guī)范并沒有特別規(guī)定線程模型Java 開發(fā)員必須編寫可在兩種模型上正確運行的程序在了解線程以及線程間
5、通訊的一些方面之后我們可以看到如何為這兩種模型設(shè)計程序線程和 Java 語言為了使用 Java 語言創(chuàng)建線程你可以生成一個Thread類或其子類的對象并給這個對象發(fā)送start(消息程序可以向任何一個派生自Runnable接口的類對象發(fā)送start(消息每個線程動作的定義包含在該線程對象的run(方法中run 方法就相當于傳統(tǒng)程序中的main(方法線程會持續(xù)運行直到run(返回為止此時該線程便死了上鎖大多數(shù)應用程序要求線程互相通信來同步它們的動作在 Java 程序中最簡單實現(xiàn)同步的方法就是上鎖為了防止同時訪問共享資源線程在使用資源的前后可以給該資源上鎖和開鎖假想給復印機上鎖任一時刻只有一個職員
6、擁有鑰匙若沒有鑰匙就不能使用復印機給共享變量上鎖就使得 Java 線程能夠快速方便地通信和同步某個線程若給一個對象上了鎖 就可以知道沒有其他線程能夠訪問該對象即使在搶占式模型中其他線程也不能夠訪問此對象直到上鎖的線程被喚醒完成工作并開鎖那些試圖訪問一個上鎖對象的線程通常會進入睡眠狀態(tài)直到上鎖的線程開鎖一旦鎖被打開這些睡眠進程就會被喚醒并移到準備就緒隊列中在 Java 編程中所有的對象都有鎖線程可以使用synchronized關(guān)鍵字來獲得鎖在任一時刻對于給定的類的實例方法或同步的代碼塊只能被一個線程執(zhí)行這是因為代碼在執(zhí)行之前要求獲得對象的鎖繼續(xù)我們關(guān)于復印機的比喻為了避免復印沖突我們可以簡單地對
7、復印資源實行同步如同下列的代碼例子任一時刻只允許一位職員使用復印資源通過使用方法在Copier對象中來修改復印機狀態(tài)這個方法就是同步方法只有一個線程能夠執(zhí)行一個Copier對象中同步代碼因此那些需要使用Copier 對象的職員就必須排隊等候class CopyMachine public synchronized void makeCopies(Document d, int nCopies /only one thread executes this at a timepublic void loadPaper( /multiple threads could access this at
8、once!synchronized(this /only one thread accesses this at a time/feel free to use shared resources, overwrite members, etc.Fine-grain 鎖在對象級使用鎖通常是一種比較粗糙的方法為什么要將整個對象都上鎖而不允許其他線程短暫地使用對象中其他同步方法來訪問共享資源如果一個對象擁有多個資源就不需要只為了讓一個線程使用其中一部分資源就將所有線程都鎖在外面由于每個對象都有鎖可以如下所示使用虛擬對象來上鎖class FineGrainLock MyMemberClass x, y
9、;Object xlock = new Object(, ylock = new Object(;public void foo( synchronized(xlock /access x here/do something here - but don't use shared resourcessynchronized(ylock /access y herepublic void bar( synchronized(this /access both x and y here/do something here - but don't use shared resourc
10、es若為了在方法級上同步不能將整個方法聲明為synchronized關(guān)鍵字它們使用的是成員鎖而不是 synchronized 方法能夠獲得的對象級鎖信號量通常情況下可能有多個線程需要訪問數(shù)目很少的資源假想在服務器上運行著若干個回答客戶端請求的線程這些線程需要連接到同一數(shù)據(jù)庫但任一時刻只能獲得一定數(shù)目的數(shù)據(jù)庫連接你要怎樣才能夠有效地將這些固定數(shù)目的數(shù)據(jù)庫連接分配給大量的線程一種控制訪問一組資源的方法除了簡單地上鎖之外就是使用眾所周知的信號量計數(shù) (counting semaphore信號量計數(shù)將一組可獲得資源的管理封裝起來信號量是在簡單上鎖的基礎(chǔ)上實現(xiàn)的,相當于能令線程安全執(zhí)行并初始化為可用資源
11、個數(shù)的計數(shù)器例如我們可以將一個信號量初始化為可獲得的數(shù)據(jù)庫連接個數(shù)一旦某個線程獲得了信號量可獲得的數(shù)據(jù)庫連接數(shù)減一線程消耗完資源并釋放該資源時計數(shù)器就會加一當信號量控制的所有資源都已被占用時若有線程試圖訪問此信號量則會進入阻塞狀態(tài)直到有可用資源被釋放信號量最常見的用法是解決消費者生產(chǎn)者問題當一個線程進行工作時若另外一個線程訪問同一共享變量就可能產(chǎn)生此問題消費者線程只能在生產(chǎn)者線程完成生產(chǎn)后才能夠訪問數(shù)據(jù)使用信號量來解決這個問題就需要創(chuàng)建一個初始化為零的信號量從而讓消費者線程訪問此信號量時發(fā)生阻塞每當完成單位工作時生產(chǎn)者線程就會向該信號量發(fā)信號釋放資源每當消費者線程消費了單位生產(chǎn)結(jié)果并需要新的數(shù)
12、據(jù)單元時它就會試圖 再次獲取信號量因此信號量的值就總是等于生產(chǎn)完畢可供消費的數(shù)據(jù)單元數(shù)這種方法比采用消費者線程不停檢查是否有可用數(shù)據(jù)單元的方法要高效得多因為消費者線程醒來后倘若沒有找到可用的數(shù)據(jù)單元就會再度進入睡眠狀態(tài)這樣的操作系統(tǒng)開銷是非常昂貴的盡管信號量并未直接被 Java 語言所支持卻很容易在給對象上鎖的基礎(chǔ)上實現(xiàn)一個簡單的實現(xiàn)方法如下所示class Semaphore private int count;public Semaphore(int n this.count = n;public synchronized void acquire( while(count = 0 try
13、wait(; catch (InterruptedException e /keep tryingcount-;public synchronized void release( count+;notify(; /alert a thread that's blocking on this semaphore常見的上鎖問題不幸的是使用上鎖會帶來其他問題讓我們來看一些常見問題以及相應的解決方法死鎖死鎖是一個經(jīng)典的多線程問題因為不同的線程都在等待那些根本不可能被釋放的鎖從而導致所有的工作都無法完成假設(shè)有兩個線程分別代表兩個饑餓的人他們必須共享刀叉并輪流吃飯他們都需要獲得兩個鎖共享刀和共享叉
14、的鎖假如線程 "A" 獲得了刀而線程 "B" 獲得了叉線 程 A 就會進入阻塞狀態(tài)來等待獲得叉而線程 B 則阻塞來等待 A 所擁有的刀這只是人為設(shè)計的例子但盡管在運行時很難探測到這類情況卻時常發(fā)生雖然要探測或推敲各種情況是非常困難的 但只要按照下面幾條規(guī)則去設(shè)計系統(tǒng)就能夠避免死鎖問題 讓所有的線程按照同樣的順序獲得一組鎖這種方法消除了 X 和 Y 的擁有者分別等待對方的資源的問題 將多個鎖組成一組并放到同一個鎖下前面死鎖的例子中可以創(chuàng)建一個銀器對象的鎖于是在獲得刀或叉之前都必須獲得這個銀器的鎖 將那些不會阻塞的可獲得資源用變量標志出來當某個線程獲得銀器對
15、象的鎖時就可以通過 檢查變量來判斷是否整個銀器集合中的對象鎖都可獲得如果是它就可以獲得相關(guān)的鎖否則就要釋放掉銀器這個鎖并稍后再嘗試 最重要的是在編寫代碼前認真仔細地設(shè)計整個系統(tǒng)多線程是困難的在開始編程之前詳細 設(shè)計系統(tǒng)能夠幫助你避免難以發(fā)現(xiàn)死鎖的問題Volatile 變量.volatile關(guān)鍵字是 Java 語言為優(yōu)化編譯器設(shè)計的以下面的代碼為例 class VolatileTest public void foo( boolean flag = false; if(flag /this could happen 一個優(yōu)化的編譯器可能會判斷出 if 部分的語句永遠不會被執(zhí)行 就根本不會編譯這
16、部分的代碼 如果這個類被多線程訪問 flag 被前面某個線程設(shè)置之后 在它被 if 語 句測試之前 可以被其他線程重新設(shè)置 用 volatile 關(guān)鍵字來聲明變量 就可以告訴 編譯器在編譯的時候 不需要通過預測變量值來優(yōu)化這部分的代碼 無法訪問的線程 有時候雖然獲取對象鎖沒有問題 IO 就是這類問題最好的例子 訪問 線程依然有可能進入阻塞狀態(tài) 在 Java 編程中 當線程因為對象內(nèi)的 IO 調(diào)用而阻塞時 此對象應當仍能被其他線程 該對象通常有責任取消這個阻塞的 IO 操作 當線程被阻塞時 造成阻塞調(diào)用的線程常常會令同步任務失敗 如 此對象也就相當于被冷凍住了 其他的線程由于 果該對象的其他方法
17、也是同步的 不能獲得對象的鎖 就不能給此對象發(fā)消息 例如 取消 IO 操作 些阻塞調(diào)用 或確認在一個用同步阻塞代碼的對象中存在非同步方法 必須確保不在同步代碼中包含那 盡管這種方法需要花費一些注 意力來保證結(jié)果代碼安全運行 但它允許在擁有對象的線程發(fā)生阻塞后 該對象仍能夠響應其他線程 為不同的線程模型進行設(shè)計 判斷是搶占式還是協(xié)作式的線程模型 取決于虛擬機的實現(xiàn)者 并根據(jù)各種實現(xiàn)而不同 因 此 Java 開發(fā)員必須編寫那些能夠在兩種模型上工作的程序 正如前面所提到的 在搶占式模型中線程可以在代碼的任何一個部分的中間被打斷 除非那 是一個原子操作代碼塊 原子操作代碼塊中的代碼段一旦開始執(zhí)行 就要
18、在該線程被換出處 理器之前執(zhí)行完畢 在 Java 編程中 分配一個小于 32 位的變量空間是一種原子操作 而 此外象 double 和 long 這兩個 64 位數(shù)據(jù)類型的分配就不是原子的 使用鎖來正確同步共享 資源的訪問 就足以保證一個多線程程序在搶占式模型下正確工作 而在協(xié)作式模型中 是否能保證線程正常放棄處理器 不掠奪其他線程的執(zhí)行時間 則完全 取決于程序員 調(diào)用 yield( 方法能夠?qū)斍暗木€程從處理器中移出到準備就緒隊列中 另 一個方法則是調(diào)用 sleep( 方法 使線程放棄處理器 并且在 sleep 方法中指定的時間間隔 內(nèi)睡眠 正如你所想的那樣 將這些方法隨意放在代碼的某個地方
19、 并不能夠保證正常工作 如果線 程正擁有一個鎖 因為它在一個同步方法或代碼塊中 則當它調(diào)用 yield( 時不能夠釋放 這個鎖 這就意味著即使這個線程已經(jīng)被掛起 等待這個鎖釋放的其他線程依然不能繼續(xù)運 行 為了緩解這個問題 最好不在同步方法中調(diào)用 yield 方法 將那些需要同步的代碼包在 一個同步塊中 里面不含有非同步的方法 并且在這些同步代碼塊之外才調(diào)用 yield 另外一個解決方法則是調(diào)用 wait( 方法 使處理器放棄它當前擁有的對象的鎖 如果對象在 方法級別上使同步的 這種方法能夠很好的工作 因為它僅僅使用了一個鎖 如果它使用 fine-grained 鎖 則 wait( 將無法放棄
20、這些鎖 此外 一個因為調(diào)用 wait( 方法而阻塞的 線程 只有當其他線程調(diào)用 notifyAll( 時才會被喚醒 線程和 AWT/Swing 在那些使用 Swing 和/或 AWT 包創(chuàng)建 GUI 用戶圖形界面 的 Java 程序中 AWT 事 件句柄在它自己的線程中運行 開發(fā)員必須注意避免將這些 GUI 線程與較耗時間的計算工 作綁在一起 因為這些線程必須負責處理用戶時間并重繪用戶圖形界面 換句話來說 一旦 GUI 線程處于繁忙 整個程序看起來就象無響應狀態(tài) Swing 線程通過調(diào)用合適方法 通 知那些 Swing callback 例如 Mouse Listener 和 Action L
21、istener 這種方法意味著 listener 無論要做多少事情 都應當利用 listener callback 方法產(chǎn)生其他線程來完成此項工 作 目的便在于讓 listener callback 更快速返回 從而允許 Swing 線程響應其他事件 如果一個 Swing 線程不能夠同步運行 響應事件并重繪輸出 那怎么能夠讓其他的線程安 全地修改 Swing 的狀態(tài) 正如上面提到的 Swing callback 在 Swing 線程中運行 因此 他們能修改 Swing 數(shù)據(jù)并繪到屏幕上 但是如果不是 Swing callback 產(chǎn)生的變化該怎么辦呢 使用一個非 Swing 線程來修改 Swing 數(shù)據(jù)是不安全的 Swing 提供了兩個方法來解決這個問題 invokeLater( 和 invokeAndWait( 為了修改 S
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 學校新員工管理制度
- 學校研討課管理制度
- 學校足球社管理制度
- 學生周末班管理制度
- 安全及風險管理制度
- 完善職代會管理制度
- 寶寶店人員管理制度
- 實驗室檢測管理制度
- 宣傳部風控管理制度
- 家具廠車間管理制度
- 15D501建筑物防雷設(shè)施安裝圖集
- 房屋安全簡易鑒定表
- 《水產(chǎn)養(yǎng)殖前沿講座》課程教學大綱
- 漁業(yè)成品油價格補助專項資金管理暫行辦法
- 卵巢交界性腫瘤課件
- 2023年06月新疆生產(chǎn)建設(shè)兵團第十二師“三支一扶”招募高校畢業(yè)生筆試題庫含答案解析
- 基于C#的WinForm程序設(shè)計學習通課后章節(jié)答案期末考試題庫2023年
- GLP-1受體激動劑的血管保護作用
- 十堰市張灣區(qū)紅衛(wèi)街道社區(qū)工作者考試真題2022
- 突發(fā)性耳聾培訓課件
- 部編版語文八年級上下冊古詩詞默寫卷合集
評論
0/150
提交評論