安全生產(chǎn)_線程進(jìn)程安全概述_第1頁(yè)
安全生產(chǎn)_線程進(jìn)程安全概述_第2頁(yè)
安全生產(chǎn)_線程進(jìn)程安全概述_第3頁(yè)
安全生產(chǎn)_線程進(jìn)程安全概述_第4頁(yè)
安全生產(chǎn)_線程進(jìn)程安全概述_第5頁(yè)
已閱讀5頁(yè),還剩39頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第三章 線程 進(jìn)程安全 進(jìn)程和線程是兩個(gè)范圍不同的概念 進(jìn)程是程序在計(jì)算機(jī)上的一次執(zhí)行活動(dòng) 運(yùn)行一個(gè)程序 相當(dāng)于啟動(dòng)了一個(gè)進(jìn)程 進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的單位 通俗地講 是一個(gè)正在執(zhí)行的程序 線程是進(jìn)程中的一個(gè)實(shí)體 是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位 它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源 一個(gè)線程可以創(chuàng)建和撤消另一個(gè)線程 同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行 比如 一個(gè)在線播放軟件 在播放歌曲的同時(shí)還可以進(jìn)行下載 就可認(rèn)為這兩件工作由不同的線程完成 線程和進(jìn)程的開(kāi)發(fā)和相關(guān)操作 在程序設(shè)計(jì)中具有重要地位 線程和進(jìn)程的安全和系統(tǒng)的安全息息相關(guān) 對(duì)于不夠熟練的程序員來(lái)說(shuō) 很容易出現(xiàn)安全隱患 而這些安全問(wèn)題又具有不間斷發(fā)生 難于調(diào)試等特點(diǎn) 一般說(shuō)來(lái) 線程的安全性主要來(lái)源于其運(yùn)行的并發(fā)性和對(duì)資源的共享性 進(jìn)程的安全性主要在應(yīng)用級(jí)別 在于其對(duì)系統(tǒng)的威脅性 不過(guò)對(duì)于系統(tǒng)軟件的開(kāi)發(fā)者 進(jìn)程安全的考慮需要更加深入 本章主要針對(duì)線程和進(jìn)程開(kāi)發(fā)過(guò)程中的安全問(wèn)題進(jìn)行講述 首先基于面向?qū)ο笳Z(yǔ)言 講解線程的的基本機(jī)制 然后講解線程操作過(guò)程中的幾個(gè)重要的安全問(wèn)題 線程同步安全 線程協(xié)作安全 線程死鎖 線程控制 最后講解進(jìn)程安全 3 1線程機(jī)制 3 1 1為什么需要線程 由于Java在線程操作方面具有較好的面向?qū)ο筇匦?也具有一定的代表性本章基于Java語(yǔ)言進(jìn)行講解 實(shí)際上 多線程最直觀的說(shuō)法是 讓?xiě)?yīng)用程序看起來(lái)好像同時(shí)能做好幾件事情 為了表達(dá)這個(gè)問(wèn)題 我們用一個(gè)案例來(lái)說(shuō)明 比如 需要在控制臺(tái)上每隔1秒鐘打印一個(gè)歡迎信息 代碼如下所示 publicclassP03 01 publicstaticvoidmain String args while true System out println Welcome try Thread sleep 1000 catch Exceptionex System out println 其他工作 代碼行1 該程序似乎沒(méi)有什么問(wèn)題 運(yùn)行時(shí) Welcome 也能不斷打印 但是 我們發(fā)現(xiàn) 打印函數(shù)中的while循環(huán)是個(gè)死循環(huán) 也就是說(shuō) 這個(gè)死循環(huán)不運(yùn)行完畢 程序?qū)⒉荒茏髌渌虑?比如 程序中的代碼行1永遠(yuǎn)也無(wú)法運(yùn)行 這就給程序的功能形成了巨大的阻礙 在實(shí)際應(yīng)用開(kāi)發(fā)的過(guò)程中 經(jīng)常會(huì)出現(xiàn)一個(gè)程序看起來(lái)同時(shí)作好幾件事情的情況 如 程序進(jìn)行一個(gè)用時(shí)較長(zhǎng)的計(jì)算 希望該計(jì)算進(jìn)行的時(shí)候 程序還可以做其他事情 程序進(jìn)行一個(gè)用時(shí)較長(zhǎng)的計(jì)算 希望該計(jì)算進(jìn)行的時(shí)候 程序還可以做其他事情 軟件要能夠接受多個(gè)客戶的請(qǐng)求 而讓客戶感覺(jué)不出等待 媒體播放器在播放歌曲的同時(shí)也能下載電影 財(cái)務(wù)軟件在后臺(tái)進(jìn)行財(cái)務(wù)匯總的同時(shí)還能接受終端的請(qǐng)求 等等 在這些情況下 多線程就能夠起到巨大的作用 線程和進(jìn)程的關(guān)系很緊密 進(jìn)程和線程是兩個(gè)不同的概念 但是進(jìn)程的范圍大于線程 通俗地說(shuō) 進(jìn)程就是一個(gè)程序 線程是這個(gè)程序能夠同時(shí)做的各件事情 比如 媒體播放機(jī)運(yùn)行時(shí)就是一個(gè)進(jìn)程 而媒體播放機(jī)同時(shí)做的下載文件和播放歌曲 就是兩個(gè)線程 以上代碼如果用線程來(lái)進(jìn)行開(kāi)發(fā) 在Java語(yǔ)言里面 就可以用如P03 02 java的方式 其他語(yǔ)言類(lèi)似 運(yùn)行 就會(huì)發(fā)現(xiàn) 此時(shí) 打印歡迎信息 和 其他工作 就 同時(shí) 做了 3 1 2線程機(jī)制和生命周期 每個(gè)程序至少自動(dòng)擁有一個(gè)線程 稱(chēng)為主線程 當(dāng)程序加載到內(nèi)存時(shí) 啟動(dòng)主線程 從上節(jié)的程序可以看出 代碼行 實(shí)際上相當(dāng)于實(shí)例化一個(gè)新的線程對(duì)象 并運(yùn)行該線程中的run 函數(shù) 該線程的運(yùn)行并不影響主線程向下執(zhí)行 這是為什么呢 這是由于多線程的機(jī)制實(shí)際上相當(dāng)于CPU交替分配給不同的代碼段來(lái)運(yùn)行 也就是說(shuō) 某一個(gè)時(shí)間片 某線程運(yùn)行 下一個(gè)時(shí)間片 另一個(gè)線程運(yùn)行 各個(gè)線程都有搶占CPU的權(quán)利 至于決定哪個(gè)線程搶占 是操作系統(tǒng)需要考慮的事情 由于時(shí)間片的輪轉(zhuǎn)非???用戶感覺(jué)不出各個(gè)線程搶占CPU的過(guò)程 看起來(lái)好像計(jì)算機(jī)在 同時(shí) 做好幾件事情 WelcomeThreadwt newWelcomeThread wt start 一個(gè)線程有從創(chuàng)建 運(yùn)行到消亡的過(guò)程 稱(chēng)為線程的生命周期 用線程的狀態(tài) state 表明線程處在生命周期的哪個(gè)階段 線程有創(chuàng)建 可運(yùn)行 運(yùn)行中 阻塞 死亡五種狀態(tài) 通過(guò)線程的控制與調(diào)度可使線程在這幾種狀態(tài)間轉(zhuǎn)化 這五種狀態(tài)詳細(xì)描述如下 1 創(chuàng)建狀態(tài) 使用new運(yùn)算符創(chuàng)建一個(gè)線程后 該線程僅僅是一個(gè)空對(duì)象 系統(tǒng)沒(méi)有分配資源 2 可運(yùn)行狀態(tài) 使用start 方法啟動(dòng)一個(gè)線程后 系統(tǒng)分配了資源 使該線程處于可運(yùn)行狀態(tài) Runnable 3 運(yùn)行中狀態(tài) 占有CPU 執(zhí)行線程的run 方法 4 阻塞狀態(tài) 運(yùn)行的線程因某種原因停止繼續(xù)運(yùn)行 5 死亡狀態(tài) 線程結(jié)束 線程的安全隱患可能出現(xiàn)在各個(gè)狀態(tài) 一般說(shuō)來(lái) 線程的安全性來(lái)源于兩個(gè)方面 1 多個(gè)線程之間可能會(huì)共享進(jìn)程的內(nèi)存資源 2 CPU的某個(gè)時(shí)間片分配給哪個(gè)線程使用 默認(rèn)情況下無(wú)法由用戶控制 多線程的安全問(wèn)題比較復(fù)雜 解決方法繁多 在這里我們闡述幾個(gè)比較典型的安全問(wèn)題 3 2線程同步安全 3 2 1線程同步 默認(rèn)情況下 線程都是獨(dú)立的 而且異步執(zhí)行 線程中包含了運(yùn)行時(shí)所需要的數(shù)據(jù)或方法 而不需要外部的資源或方法 也不必關(guān)心其它線程的狀態(tài)或行為 但是在多個(gè)線程在運(yùn)行時(shí)共享數(shù)據(jù)的情況下 就需考慮其他線程的狀態(tài)和行為 否則就不能保證程序的運(yùn)行結(jié)果的正確性 在某些項(xiàng)目中 經(jīng)常會(huì)出現(xiàn)線程同步的問(wèn)題 即 多個(gè)線程在訪問(wèn)同一資源時(shí) 會(huì)出現(xiàn)安全問(wèn)題 本節(jié)基于一個(gè)簡(jiǎn)單的案例 針對(duì)線程的同步問(wèn)題進(jìn)行闡述 所謂同步 就是在發(fā)出一個(gè)功能調(diào)用時(shí) 在沒(méi)有得到結(jié)果之前 該調(diào)用就不返回 同時(shí)其它線程也不能調(diào)用這個(gè)方法 通俗地講 一個(gè)線程是否能夠搶占CPU 必須考慮另一個(gè)線程中的某種條件 而不能隨便讓操作系統(tǒng)按照默認(rèn)方式分配CPU 如果條件不具備 就應(yīng)該等待另一個(gè)線程運(yùn)行 直到條件具備 3 2 2案例分析 給出一個(gè)案例 有若干張飛機(jī)票 2個(gè)線程去賣(mài)它們 要求沒(méi)有票時(shí)能夠提示 沒(méi)有票了 以最后剩下3張票為例 首先用傳統(tǒng)方法來(lái)編寫(xiě)這段代碼 代碼如P03 03 java所示 運(yùn)行 控制臺(tái)打印如下 這段程序貌似沒(méi)有問(wèn)題 但是它是很不安全的 并且這種不安全性很難發(fā)現(xiàn) 會(huì)給項(xiàng)目后期維護(hù)帶來(lái)巨大的代價(jià) 觀察程序中的代碼行1處的注釋 當(dāng)只剩下一張票時(shí) 線程1賣(mài)出了最后一張票 接著要運(yùn)行ticketNum 但在ticketNum 還沒(méi)來(lái)得及運(yùn)行的時(shí)候 線程2有可能搶占CPU 來(lái)判斷當(dāng)前有無(wú)票可賣(mài) 此時(shí) 由于線程1還沒(méi)有將ticketNum 當(dāng)然票數(shù)還是1 線程2判斷還可以買(mǎi)票 這樣 最后一張票賣(mài)出了兩次 當(dāng)然 上面的程序中 沒(méi)有給線程2以買(mǎi)票的機(jī)會(huì) 實(shí)際上票都由線程1賣(mài)出 我們看不出其中的問(wèn)題 為了讓大家看清這個(gè)問(wèn)題 我們模擬線程1和線程2交替賣(mài)票的情況 將P03 03 java的代碼改為P03 04 java 該代碼中 增加了一行 程序休眠1000毫秒 讓另一個(gè)線程來(lái)?yè)屨糃PU 運(yùn)行 控制臺(tái)打印如下 最后一張票被賣(mài)出兩次 系統(tǒng)不可靠 更為嚴(yán)重的是 該問(wèn)題的出現(xiàn)很具有隨機(jī)性 比如 有些項(xiàng)目在實(shí)驗(yàn)室運(yùn)行階段沒(méi)有問(wèn)題 因?yàn)槟膫€(gè)線程搶占CPU 是由操作系統(tǒng)決定的 用戶并沒(méi)有權(quán)利干涉 也無(wú)法預(yù)測(cè) 所以 項(xiàng)目可能在商業(yè)運(yùn)行階段出現(xiàn)了問(wèn)題 等到維護(hù)人員去查問(wèn)題的時(shí)候 由于問(wèn)題出現(xiàn)的隨機(jī)性 問(wèn)題可能就不出現(xiàn)了 這種工作往往給維護(hù)帶來(lái)了巨大的代價(jià) 以上案例是多個(gè)線程消費(fèi)有限資源的情況 該情況下還有很多其他案例 如 多個(gè)線程 向有限空間寫(xiě)數(shù)據(jù)時(shí) 線程1寫(xiě)完數(shù)據(jù) 空間滿了 但沒(méi)來(lái)得及告訴系統(tǒng) 此時(shí)另一個(gè)線程搶占CPU 也來(lái)寫(xiě) 不知道空間已滿 造成溢出 3 2 3解決方案 怎樣解決這個(gè)問(wèn)題 很簡(jiǎn)單 就是讓一個(gè)線程賣(mài)票時(shí)其他線程不能搶占CPU 根據(jù)定義 實(shí)際上相當(dāng)于要實(shí)現(xiàn)線程的同步 通俗地講 可以給共享資源 在本例中為票 加一把鎖 這把鎖只有一把鑰匙 哪個(gè)線程獲取了這把鑰匙 才有權(quán)利訪問(wèn)該共享資源 有一種比較直觀的方法 可以在共享資源 如 票 每一個(gè)對(duì)象內(nèi)部都增加一個(gè)新成員 標(biāo)識(shí) 票 是否正在被賣(mài)中 其他線程訪問(wèn)時(shí) 必須檢查這個(gè)標(biāo)識(shí) 如果這個(gè)標(biāo)識(shí)確定票正在被賣(mài)中 線程不能搶占CPU 這種設(shè)計(jì)理論上當(dāng)然也是可行 但由于線程同步的情況并不是很普遍 僅僅為了這種小概率事件 在所有對(duì)象內(nèi)部都開(kāi)辟另一個(gè)成員空間 帶來(lái)極大的空間浪費(fèi) 增加了編程難度 所以 一般不采用這種方法 現(xiàn)代的編程語(yǔ)言的設(shè)計(jì)思路都是把同步標(biāo)識(shí)加在代碼段上 確切的說(shuō) 是把同步標(biāo)識(shí)放在 訪問(wèn)共享資源 如賣(mài)票 的代碼段 上 不同語(yǔ)言中 同步代碼段的實(shí)現(xiàn)模型類(lèi)似 只是表達(dá)方式有些不同 這里以Java語(yǔ)言為例 在Java語(yǔ)言中 synchronized關(guān)鍵字可以解決這個(gè)問(wèn)題 整個(gè)語(yǔ)法形式表現(xiàn)為 注意 synchronized后的 同步鎖對(duì)象 必須是可以被各個(gè)線程共享的 如this 某個(gè)全局標(biāo)量等 不能是一個(gè)局部變量 其原理為 當(dāng)某一線程運(yùn)行同步代碼段時(shí) 在 同步鎖對(duì)象 上置一標(biāo)記 運(yùn)行完這段代碼 標(biāo)記消除 其他線程要想搶占CPU運(yùn)行這段代碼 必須在 同步鎖對(duì)象 上先檢查該標(biāo)記 只有標(biāo)記處于消除狀態(tài) 才能搶占CPU 在上面的例子中 this是一個(gè) 同步鎖對(duì)象 synchronized 同步鎖對(duì)象 訪問(wèn)共享資源 需要同步的代碼段 因此 在上面的案例中 可以將將賣(mài)票的代碼用synchronized代碼塊包圍起來(lái) 同步鎖對(duì)象 取this 如代碼P03 05 java所示 運(yùn)行 可以得到如下效果 這說(shuō)明程序運(yùn)行完全正常 從以上代碼可以看出 該方法的本質(zhì)是將需要獨(dú)占CPU的代碼用synchronized this 包圍起來(lái) 如前所述 一個(gè)線程進(jìn)入這段代碼之后 就在this上加了一個(gè)標(biāo)記 直到該線程將這段代碼運(yùn)行完畢 才釋放這個(gè)標(biāo)記 如果其他線程想要搶占CPU 先要檢查this上是否有這個(gè)標(biāo)記 若有 就必須等待 但是可以看出 該代碼實(shí)際上運(yùn)行較慢 因?yàn)橐粋€(gè)線程的運(yùn)行 必須等待另一個(gè)線程將同步代碼段運(yùn)行完畢 因此 從性能上講 線程同步是非常耗費(fèi)資源的一種操作 我們要盡量控制線程同步的代碼段范圍 理論上說(shuō) 同步的代碼段范圍越小 段數(shù)越少越好 因此在某些情況下 推薦將小的同步代碼段合并為大的同步代碼段 實(shí)際上 在Java內(nèi) 還可以直接把synchronized關(guān)鍵字直接加在函數(shù)的定義上 這也是一種可以推薦的方法 不過(guò) 值得一提的是 如果不能確定整個(gè)函數(shù)都需要同步 那就要盡量避免直接把synchronized加在函數(shù)定義上的做法 如前所述 要控制同步粒度 同步的代碼段越小越好 synchronized控制的范圍越小越好 否則造成不必要的系統(tǒng)開(kāi)銷(xiāo) 所以 在實(shí)際開(kāi)發(fā)的過(guò)程中 要十分小心 因?yàn)檫^(guò)多的線程等待可能造成系統(tǒng)性能的下降 甚至造成死鎖 3 3線程協(xié)作安全 3 3 1線程協(xié)作 有些情況下 多個(gè)線程合作完成一件事情的幾個(gè)步驟 此時(shí)線程之間實(shí)現(xiàn)了協(xié)作 如一個(gè)工作需要若干個(gè)步驟 各個(gè)步驟都比較耗時(shí) 不能因?yàn)樗鼈兊倪\(yùn)行 影響程序的運(yùn)行效果 最好的方法就是將各步用線程實(shí)現(xiàn) 但是 由于線程隨時(shí)都有可能搶占CPU 可能在前面一個(gè)步驟沒(méi)有完成時(shí) 后面的步驟線程就已經(jīng)運(yùn)行 該安全隱患造成系統(tǒng)得不到正確結(jié)果 3 3 2案例分析 給出一個(gè)案例 線程1負(fù)責(zé)完成一個(gè)復(fù)雜運(yùn)算 比較耗時(shí) 線程2負(fù)責(zé)得到結(jié)果 并將結(jié)果進(jìn)行下一步處理 如 某個(gè)科學(xué)計(jì)算系統(tǒng)中 線程1負(fù)責(zé)計(jì)算1 1000各個(gè)數(shù)字的和 暫且認(rèn)為它非常耗時(shí) 線程2負(fù)責(zé)得到這個(gè)結(jié)果并且寫(xiě)入數(shù)據(jù)庫(kù) 讀者首先想到的是將耗時(shí)的計(jì)算放入線程 這是正確的想法 首先用傳統(tǒng)線程方法來(lái)編寫(xiě)這段代碼 代碼如P03 06 java所示 該程序貌似沒(méi)有問(wèn)題 也能夠打印正確結(jié)果 但是和上一節(jié)的例子一樣 它也是很不安全的 這種不安全性也很難發(fā)現(xiàn) 也會(huì)給項(xiàng)目后期維護(hù)帶來(lái)巨大的代價(jià) 該程序的安全隱患在哪里呢 觀察cal 函數(shù)中的代碼 當(dāng)線程th1運(yùn)行后 線程th2運(yùn)行 此時(shí) 線程th2隨時(shí)可能搶占CPU 而不一定要等線程th1運(yùn)行完畢 當(dāng)然 在上面的例子中 可能因?yàn)榫€程th1運(yùn)行較快 th2在它運(yùn)行的過(guò)程中沒(méi)有搶占CPU 碰巧 得到了正確結(jié)果 但是如果讓線程th2搶占CPU 這樣 系統(tǒng)可能得不到正確結(jié)果 為了解釋這個(gè)問(wèn)題 將P03 06 java的代碼改為P03 07 java 該代碼中 增加了一行 程序休眠1毫秒 讓另一個(gè)線程來(lái)?yè)屨糃PU 運(yùn)行 控制臺(tái)打印如下 很顯然 這個(gè)結(jié)果不是我們所需要的 那為什么sum得到的結(jié)果為1呢 很明顯 線程th1的start函數(shù)運(yùn)行時(shí) 相當(dāng)于讓求和過(guò)程以多線程形式運(yùn)行 在它 休眠 之際 th2就搶占了CPU 在求和還沒(méi)開(kāi)始做或只完成一部分時(shí)就打印sum 導(dǎo)致得到不正常結(jié)果 3 3 3解決方案 怎樣解決 顯而易見(jiàn) 方法是 在運(yùn)行一個(gè)線程時(shí) 命令其他線程等待該線程運(yùn)行完畢 才能搶占CPU進(jìn)行運(yùn)行 對(duì)于該問(wèn)題 不同語(yǔ)言解決方法類(lèi)似 以Java語(yǔ)言為例 在Java語(yǔ)言中 線程的join 方法可以解決這個(gè)問(wèn)題 見(jiàn)代碼P03 08 java運(yùn)行正常 實(shí)際上 該程序相當(dāng)于摒棄了 線程就是為了程序看起來(lái)同時(shí)做好幾件事情 的思想 將并發(fā)程序又變成了順序的 如果線程th1沒(méi)有運(yùn)行完畢的話 程序會(huì)在th join 處堵塞 如果cal 函數(shù)耗時(shí)較長(zhǎng) 程序?qū)⒁恢钡却?一般的方法是 可以將該工作放在另一個(gè)線程中 這樣 既不會(huì)堵塞主程序 又能夠保證數(shù)據(jù)安全性 見(jiàn)代碼P03 09 java 3 4線程死鎖安全 3 4 1線程死鎖 死鎖 DeadLock 是指兩個(gè)或兩個(gè)以上的線程在執(zhí)行過(guò)程中 因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象 此時(shí)稱(chēng)系統(tǒng)處于死鎖狀態(tài) 這些永遠(yuǎn)在互相等待的線程稱(chēng)為死鎖線程 產(chǎn)生死鎖的四個(gè)必要條件是 互斥條件 資源每次只能被一個(gè)線程使用 如前面的 線程同步代碼段 就是只能被一個(gè)線程使用的典型資源 請(qǐng)求與保持條件 一個(gè)線程請(qǐng)求資源 但因?yàn)槟撤N原因 該資源無(wú)法分配給它 于是該線程阻塞 此時(shí) 它對(duì)已獲得的資源保持不放 不剝奪條件 進(jìn)程已獲得的資源 在未使用完之前 不管其是否阻塞 無(wú)法強(qiáng)行剝奪 循環(huán)等待條件 若干進(jìn)程之間互相等待 形成一種頭尾相接的循環(huán)等待資源關(guān)系 這四個(gè)條件是死鎖的必要條件 只要系統(tǒng)發(fā)生死鎖 這些條件必然成立 而只要上述條件之一不滿足 就不會(huì)發(fā)生死鎖 3 4 2案例分析 以Java語(yǔ)言為例 死鎖一般來(lái)源于代碼段的同步 當(dāng)一段同步代碼被某線程運(yùn)行時(shí) 其他線程可能進(jìn)入堵塞狀態(tài) 無(wú)法搶占CPU 而剛好在該線程中 訪問(wèn)了某個(gè)對(duì)象 此時(shí) 除非同步鎖定被解除 否則其他線程就不能訪問(wèn)那個(gè)對(duì)象 這可以稱(chēng)為 線程正在等待一個(gè)對(duì)象資源 如果出現(xiàn)一種極端情況 一個(gè)線程等候另一個(gè)對(duì)象 而另一個(gè)對(duì)象又在等候下一個(gè)對(duì)象 以此類(lèi)推 這個(gè) 等候鏈 如果進(jìn)入封閉狀態(tài) 也就是說(shuō) 最后那個(gè)對(duì)象等候的是第一個(gè)對(duì)象 此時(shí) 所有線程都會(huì)陷入無(wú)休止的相互等待狀態(tài) 造成死鎖 盡管這種情況并非經(jīng)常出現(xiàn) 但一旦碰到 程序的調(diào)試將變得異常艱難 在這里給出一個(gè)死鎖的案例 如代碼P03 10 java 這段程序也貌似沒(méi)有問(wèn)題 但是和上一節(jié)的例子一樣 它也是很不安全的 這種不安全性也很難發(fā)現(xiàn) 觀察run 函數(shù)中的代碼 當(dāng)th1運(yùn)行后 進(jìn)入代碼段1 鎖定了S1 如果此時(shí)th2運(yùn)行 搶占CPU 進(jìn)入代碼段3 鎖定S2 那么th1就無(wú)法運(yùn)行代碼段2 但是又沒(méi)有釋放S1 此時(shí) th2也就不能運(yùn)行代碼段4 造成互相等待 為了模擬這個(gè)過(guò)程 我們?cè)诔绦蛑性黾幼屍湫菝叩拇a 好讓另一個(gè)線程來(lái)?yè)屨糃PU 將P03 10 java的代碼改為P03 11 java該代碼中 增加了一行 程序休眠1000毫秒 讓另一個(gè)線程來(lái)?yè)屨糃PU 運(yùn)行 控制臺(tái)打印如下 兩個(gè)線程陷入無(wú)休止的等待 其原因是 線程th1進(jìn)入代碼段1后 線程2搶占CPU 鎖定了S2 而線程th1對(duì)S1的鎖定又沒(méi)有解除 造成線程th2無(wú)法運(yùn)行下去 當(dāng)然 由于線程th2鎖定了S2 線程th1也無(wú)法運(yùn)行下去 死鎖是一個(gè)很重要的問(wèn)題 它能導(dǎo)致整個(gè)應(yīng)用程序慢慢終止 尤其是當(dāng)開(kāi)發(fā)人員不熟悉如何分析死鎖環(huán)境的時(shí)候 還很難被分離和修復(fù) 3 4 3解決方案 就語(yǔ)言本身來(lái)說(shuō) 尚未直接提供防止死鎖的幫助措施 需要我們通過(guò)謹(jǐn)慎的設(shè)計(jì)來(lái)避免 一般情況下 我們主要是針對(duì)死鎖產(chǎn)生的四個(gè)必要條件來(lái)進(jìn)行破壞 用以避免和預(yù)防死鎖 在系統(tǒng)設(shè)計(jì) 線程開(kāi)發(fā)等方面 注意如何不讓這四個(gè)必要條件成立 如何確定資源的合理分配算法 避免線程永久占據(jù)系統(tǒng)資源 以Java為例 Java并不提供對(duì)死鎖的檢測(cè)機(jī)制 但可以通過(guò)javathreaddump來(lái)進(jìn)行判斷 一般情況下 當(dāng)死鎖發(fā)生時(shí) Java虛擬機(jī)處于掛起狀態(tài) threaddump可以給出靜態(tài)穩(wěn)定的信息 從操作系統(tǒng)上觀察 虛擬機(jī)的CPU占用率為零 這時(shí)可以收集threaddump 查找 waitingformonitorentry 的線程 如果大量thread都在等待給同一個(gè)地址上鎖 說(shuō)明很可能死鎖發(fā)生了 解決死鎖沒(méi)有簡(jiǎn)單的方法 這是因?yàn)榫€程產(chǎn)生死鎖都各有各的原因 而且往往具有很高的負(fù)載 從技術(shù)上講 可以用如下方法來(lái)進(jìn)行死鎖排除 可以撤消陷于死鎖的全部線程 可以逐個(gè)撤消陷于死鎖的進(jìn)程 直到死鎖不存在 從陷于死鎖的線程中逐個(gè)強(qiáng)迫放棄所占用的資源 直至死鎖消失 提示 關(guān)于死鎖的檢測(cè)與解除 有很多重要算法 如資源分配算法 銀行家算法等 在操作系統(tǒng)的一些參考資料中應(yīng)該可以進(jìn)行足夠了解 很多軟件產(chǎn)品內(nèi)置了死鎖解決策略 可做參考 如 數(shù)據(jù)庫(kù)死鎖 一個(gè)連接占用了另一個(gè)連接所需的數(shù)據(jù)庫(kù)鎖 它可能阻塞另一個(gè)連接 如果兩個(gè)或兩個(gè)以上的連接相互阻塞 產(chǎn)生死鎖 該情況下 一般會(huì)強(qiáng)制銷(xiāo)毀一個(gè)連接 通常是使用最少的連接 并回滾其事務(wù) 這將釋放所有與已經(jīng)結(jié)束的事務(wù)相關(guān)聯(lián)的鎖 至少允許其他連接中有一個(gè)可以獲取它們正在被阻塞的鎖 資源池耗盡死鎖 資源池太小 而每個(gè)線程需要的資源超過(guò)了池中的可用資源 產(chǎn)生死鎖 此時(shí)可以增加連接池的大小或者重構(gòu)代碼 以便單個(gè)線程不需要同時(shí)使用很多資源 3 5線程控制安全 3 5 1安全隱患 線程控制主要是對(duì)線程生命周期的一些操作 如暫停 繼續(xù) 消亡等 本節(jié)以Java語(yǔ)言為例 講解線程控制中的一些安全問(wèn)題 Java中提供了對(duì)線程生命周期進(jìn)行控制的函數(shù) stop 停止線程 suspend 暫停線程的運(yùn)行 resume 繼續(xù)線程的運(yùn)行 destroy 讓線程銷(xiāo)毀 等等 線程生命周期中的安全問(wèn)題主要體現(xiàn)在 線程暫?;蛘呓K止時(shí) 可能對(duì)某些資源的鎖并沒(méi)有釋放 它所保持的任何資源都會(huì)保持鎖定狀態(tài) 線程暫停之后 我們無(wú)法預(yù)計(jì)它什么時(shí)候會(huì)繼續(xù) 一般和用戶操作有關(guān) 如果對(duì)某個(gè)資源的鎖長(zhǎng)期被保持 其他線程在任何時(shí)候都無(wú)法再次訪問(wèn)該資源 極有可能造成死鎖 針對(duì)這個(gè)問(wèn)題 為減少出現(xiàn)死鎖的可能 Java1 2中 將Thread的stop suspend resume 以及destroy 方法定義為 已過(guò)時(shí) 方法 不再推薦使用 3 5 2案例分析 如前所述 線程的暫停和繼續(xù) 早期采用suspend 和resume 方法 但是容易發(fā)生死鎖 以線程暫停為例 調(diào)用suspend 的時(shí)候 目標(biāo)線程會(huì)停下來(lái) 但卻仍然持有在這之前獲得的鎖定 此時(shí) 其他任何線程都不能訪問(wèn)鎖定的資源 除非被 掛起 的線程恢復(fù)運(yùn)行 如果它們想恢復(fù)目標(biāo)線程 同時(shí)又試圖使用任何一個(gè)鎖定的資源 就會(huì)造成死鎖 下面給出一個(gè)案例 來(lái)說(shuō)明這個(gè)問(wèn)題 屏幕上不斷打印歡迎信息 點(diǎn)擊按鈕 打印工作暫停 再點(diǎn)擊 繼續(xù)打印 傳統(tǒng)代碼如P03 12 java如果點(diǎn)擊 暫停 按鈕 則暫停打印 再點(diǎn)擊 繼續(xù)打印 如上所述 該代碼實(shí)際上在事件響應(yīng)中用suspend 和resume 來(lái)控制線程的暫停和繼續(xù) 是不安全的 3 5 3解決方案 解決該問(wèn)題 常見(jiàn)的方法有如下幾種 1 當(dāng)需要暫停時(shí) 干脆讓線程的run 方法結(jié)束運(yùn)行以釋放資源 實(shí)際上就是讓該線程永久結(jié)束 繼續(xù)時(shí) 新開(kāi)辟一個(gè)線程繼續(xù)工作 怎樣讓run 方法結(jié)束呢 一般可用一個(gè)標(biāo)志告訴線程什么時(shí)候通過(guò)退出自己的run 方法來(lái)中止自己的執(zhí)行 2 將線程暫?;蚶^續(xù) 不使用suspend 和resume 可在Thread類(lèi)中置入一個(gè)標(biāo)志 指出線程應(yīng)該活動(dòng)還是掛起 若標(biāo)志指出線程應(yīng)該掛起 便用wait 命其進(jìn)入等待狀態(tài) 若標(biāo)志指出線程應(yīng)當(dāng)恢復(fù) 則用一個(gè)notify 重新啟動(dòng)線程 3 不推薦使用stop 來(lái)終止阻塞的線程 而應(yīng)換用由Thread提供的interrupt 方法 以便中止并退出堵塞的代碼 3 6進(jìn)程安全 3 6 1進(jìn)程概述 進(jìn)程是一個(gè)執(zhí)行中的程序 對(duì)每一個(gè)進(jìn)程來(lái)說(shuō) 都有自己獨(dú)立的一片內(nèi)存空間和一組系統(tǒng)資源 進(jìn)程由進(jìn)程控制塊 程序段 數(shù)據(jù)段三部分組成 在進(jìn)程概念中 每一個(gè)進(jìn)程的內(nèi)部數(shù)據(jù)和狀態(tài)都是完全獨(dú)立的 一個(gè)進(jìn)程可以包含若干線程 線程可以幫助應(yīng)用程序同時(shí)做幾件事 比如一個(gè)線程向磁盤(pán)寫(xiě)入文件

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論