




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、編寫(xiě)多線程的 Java 應(yīng)用程序如何避免當(dāng)前編程中最常見(jiàn)的問(wèn)題Alex Roetter (aroetterCS.STeton Data Systems 的軟件工程師2001 年 2 月Java Thread API 允許程序員編寫(xiě)具有多處理機(jī)制優(yōu)點(diǎn)的應(yīng)用程序在后臺(tái)處理任務(wù)的同時(shí)保持用戶所需的交互感Alex Roetter 介紹了Java Thread API并概述多線程可能引起的問(wèn)題以及常見(jiàn)問(wèn)題的解決方案幾乎所有使用 AWT 或 Swing 編寫(xiě)的畫(huà)圖程序都需要多線程但多線程程序會(huì)造成許多困難剛開(kāi)始編程的開(kāi)發(fā)者常常會(huì)發(fā)現(xiàn)他們被一些問(wèn)題所折磨例如不正確的程序行為或死鎖在本文
2、中我們將探討使用多線程時(shí)遇到的問(wèn)題并提出那些常見(jiàn)陷阱的解決方案線程是什么 一個(gè)程序或進(jìn)程能夠包含多個(gè)線程這些線程可以根據(jù)程序的代碼執(zhí)行相應(yīng)的指令多線程看上去似乎在并行執(zhí)行它們各自的工作就像在一臺(tái)計(jì)算機(jī)上運(yùn)行著多個(gè)處理機(jī)一樣在多處理機(jī)計(jì)算機(jī)上實(shí)現(xiàn)多線程時(shí)它們確實(shí)可以并行工作和進(jìn)程不同的是線程共享地址空間也就是說(shuō)多個(gè)線程能夠讀寫(xiě)相同的變量或數(shù)據(jù)結(jié)構(gòu)編寫(xiě)多線程程序時(shí)你必須注意每個(gè)線程是否干擾了其他線程的工作可以將程序看作一個(gè)辦公室如果不需要共享辦公室資源或與其他人交流所有職員就會(huì)獨(dú)立并行地工作某個(gè)職員若要和其他人交談當(dāng)且僅當(dāng)該職員在聽(tīng)且他們兩說(shuō)同樣的語(yǔ)言此外只有在復(fù)印機(jī)空閑且處于可用狀態(tài)沒(méi)有僅完成一
3、半的復(fù)印工作沒(méi)有紙張阻塞等問(wèn)題時(shí)職員才能夠使用它在這篇文章中你將看到在 Java 程序中互相協(xié)作的線程就好像是在一個(gè)組織良好的機(jī)構(gòu)中工作的職員在多線程程序中線程可以從準(zhǔn)備就緒隊(duì)列中得到并在可獲得的系統(tǒng) CPU 上運(yùn)行操作系統(tǒng)可以將線程從處理器移到準(zhǔn)備就緒隊(duì)列或阻塞隊(duì)列中這種情況可以認(rèn)為是處理器掛起了該線程同樣Java 虛擬機(jī) (JVM 也可以控制線程的移動(dòng)在協(xié)作或搶先模型中從準(zhǔn)備就緒隊(duì)列中將進(jìn)程移到處理器中于是該線程就可以開(kāi)始執(zhí)行它的程序代碼協(xié)作式線程模型允許線程自己決定什么時(shí)候放棄處理器來(lái)等待其他的線程程序開(kāi)發(fā)員可以精確地決定某個(gè)線程何時(shí)會(huì)被其他線程掛起允許它們與對(duì)方有效地合作缺點(diǎn)在于某些惡
4、意或是寫(xiě)得不好的線程會(huì)消耗所有可獲得的 CPU 時(shí)間導(dǎo)致其他線程饑餓 在搶占式線程模型中操作系統(tǒng)可以在任何時(shí)候打斷線程通常會(huì)在它運(yùn)行了一段時(shí)間就是所謂的一個(gè)時(shí)間片后才打斷它這樣的結(jié)果自然是沒(méi)有線程能夠不公平地長(zhǎng)時(shí)間霸占處理器然而隨時(shí)可能打斷線程就會(huì)給程序開(kāi)發(fā)員帶來(lái)其他麻煩同樣使用辦公室的例子假設(shè)某個(gè)職員搶在另一人前使用復(fù)印機(jī)但打印工作在未完成的時(shí)候離開(kāi)了另一人接著使用復(fù)印機(jī)時(shí)該復(fù)印機(jī)上可能就還有先前那名職員留下來(lái)的資料搶占式線程模型要求線程正確共享資源協(xié)作式模型卻要求線程共享執(zhí)行時(shí)間由于 JVM 規(guī)范并沒(méi)有特別規(guī)定線程模型Java 開(kāi)發(fā)員必須編寫(xiě)可在兩種模型上正確運(yùn)行的程序在了解線程以及線程間
5、通訊的一些方面之后我們可以看到如何為這兩種模型設(shè)計(jì)程序線程和 Java 語(yǔ)言為了使用 Java 語(yǔ)言創(chuàng)建線程你可以生成一個(gè)Thread類或其子類的對(duì)象并給這個(gè)對(duì)象發(fā)送start(消息程序可以向任何一個(gè)派生自Runnable接口的類對(duì)象發(fā)送start(消息每個(gè)線程動(dòng)作的定義包含在該線程對(duì)象的run(方法中run 方法就相當(dāng)于傳統(tǒng)程序中的main(方法線程會(huì)持續(xù)運(yùn)行直到run(返回為止此時(shí)該線程便死了上鎖大多數(shù)應(yīng)用程序要求線程互相通信來(lái)同步它們的動(dòng)作在 Java 程序中最簡(jiǎn)單實(shí)現(xiàn)同步的方法就是上鎖為了防止同時(shí)訪問(wèn)共享資源線程在使用資源的前后可以給該資源上鎖和開(kāi)鎖假想給復(fù)印機(jī)上鎖任一時(shí)刻只有一個(gè)職員
6、擁有鑰匙若沒(méi)有鑰匙就不能使用復(fù)印機(jī)給共享變量上鎖就使得 Java 線程能夠快速方便地通信和同步某個(gè)線程若給一個(gè)對(duì)象上了鎖 就可以知道沒(méi)有其他線程能夠訪問(wèn)該對(duì)象即使在搶占式模型中其他線程也不能夠訪問(wèn)此對(duì)象直到上鎖的線程被喚醒完成工作并開(kāi)鎖那些試圖訪問(wèn)一個(gè)上鎖對(duì)象的線程通常會(huì)進(jìn)入睡眠狀態(tài)直到上鎖的線程開(kāi)鎖一旦鎖被打開(kāi)這些睡眠進(jìn)程就會(huì)被喚醒并移到準(zhǔn)備就緒隊(duì)列中在 Java 編程中所有的對(duì)象都有鎖線程可以使用synchronized關(guān)鍵字來(lái)獲得鎖在任一時(shí)刻對(duì)于給定的類的實(shí)例方法或同步的代碼塊只能被一個(gè)線程執(zhí)行這是因?yàn)榇a在執(zhí)行之前要求獲得對(duì)象的鎖繼續(xù)我們關(guān)于復(fù)印機(jī)的比喻為了避免復(fù)印沖突我們可以簡(jiǎn)單地對(duì)
7、復(fù)印資源實(shí)行同步如同下列的代碼例子任一時(shí)刻只允許一位職員使用復(fù)印資源通過(guò)使用方法在Copier對(duì)象中來(lái)修改復(fù)印機(jī)狀態(tài)這個(gè)方法就是同步方法只有一個(gè)線程能夠執(zhí)行一個(gè)Copier對(duì)象中同步代碼因此那些需要使用Copier 對(duì)象的職員就必須排隊(duì)等候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 鎖在對(duì)象級(jí)使用鎖通常是一種比較粗糙的方法為什么要將整個(gè)對(duì)象都上鎖而不允許其他線程短暫地使用對(duì)象中其他同步方法來(lái)訪問(wèn)共享資源如果一個(gè)對(duì)象擁有多個(gè)資源就不需要只為了讓一個(gè)線程使用其中一部分資源就將所有線程都鎖在外面由于每個(gè)對(duì)象都有鎖可以如下所示使用虛擬對(duì)象來(lái)上鎖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若為了在方法級(jí)上同步不能將整個(gè)方法聲明為synchronized關(guān)鍵字它們使用的是成員鎖而不是 synchronized 方法能夠獲得的對(duì)象級(jí)鎖信號(hào)量通常情況下可能有多個(gè)線程需要訪問(wèn)數(shù)目很少的資源假想在服務(wù)器上運(yùn)行著若干個(gè)回答客戶端請(qǐng)求的線程這些線程需要連接到同一數(shù)據(jù)庫(kù)但任一時(shí)刻只能獲得一定數(shù)目的數(shù)據(jù)庫(kù)連接你要怎樣才能夠有效地將這些固定數(shù)目的數(shù)據(jù)庫(kù)連接分配給大量的線程一種控制訪問(wèn)一組資源的方法除了簡(jiǎn)單地上鎖之外就是使用眾所周知的信號(hào)量計(jì)數(shù) (counting semaphore信號(hào)量計(jì)數(shù)將一組可獲得資源的管理封裝起來(lái)信號(hào)量是在簡(jiǎn)單上鎖的基礎(chǔ)上實(shí)現(xiàn)的,相當(dāng)于能令線程安全執(zhí)行并初始化為可用資源
11、個(gè)數(shù)的計(jì)數(shù)器例如我們可以將一個(gè)信號(hào)量初始化為可獲得的數(shù)據(jù)庫(kù)連接個(gè)數(shù)一旦某個(gè)線程獲得了信號(hào)量可獲得的數(shù)據(jù)庫(kù)連接數(shù)減一線程消耗完資源并釋放該資源時(shí)計(jì)數(shù)器就會(huì)加一當(dāng)信號(hào)量控制的所有資源都已被占用時(shí)若有線程試圖訪問(wèn)此信號(hào)量則會(huì)進(jìn)入阻塞狀態(tài)直到有可用資源被釋放信號(hào)量最常見(jiàn)的用法是解決消費(fèi)者生產(chǎn)者問(wèn)題當(dāng)一個(gè)線程進(jìn)行工作時(shí)若另外一個(gè)線程訪問(wèn)同一共享變量就可能產(chǎn)生此問(wèn)題消費(fèi)者線程只能在生產(chǎn)者線程完成生產(chǎn)后才能夠訪問(wèn)數(shù)據(jù)使用信號(hào)量來(lái)解決這個(gè)問(wèn)題就需要?jiǎng)?chuàng)建一個(gè)初始化為零的信號(hào)量從而讓消費(fèi)者線程訪問(wèn)此信號(hào)量時(shí)發(fā)生阻塞每當(dāng)完成單位工作時(shí)生產(chǎn)者線程就會(huì)向該信號(hào)量發(fā)信號(hào)釋放資源每當(dāng)消費(fèi)者線程消費(fèi)了單位生產(chǎn)結(jié)果并需要新的數(shù)
12、據(jù)單元時(shí)它就會(huì)試圖 再次獲取信號(hào)量因此信號(hào)量的值就總是等于生產(chǎn)完畢可供消費(fèi)的數(shù)據(jù)單元數(shù)這種方法比采用消費(fèi)者線程不停檢查是否有可用數(shù)據(jù)單元的方法要高效得多因?yàn)橄M(fèi)者線程醒來(lái)后倘若沒(méi)有找到可用的數(shù)據(jù)單元就會(huì)再度進(jìn)入睡眠狀態(tài)這樣的操作系統(tǒng)開(kāi)銷是非常昂貴的盡管信號(hào)量并未直接被 Java 語(yǔ)言所支持卻很容易在給對(duì)象上鎖的基礎(chǔ)上實(shí)現(xiàn)一個(gè)簡(jiǎn)單的實(shí)現(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常見(jiàn)的上鎖問(wèn)題不幸的是使用上鎖會(huì)帶來(lái)其他問(wèn)題讓我們來(lái)看一些常見(jiàn)問(wèn)題以及相應(yīng)的解決方法死鎖死鎖是一個(gè)經(jīng)典的多線程問(wèn)題因?yàn)椴煌木€程都在等待那些根本不可能被釋放的鎖從而導(dǎo)致所有的工作都無(wú)法完成假設(shè)有兩個(gè)線程分別代表兩個(gè)饑餓的人他們必須共享刀叉并輪流吃飯他們都需要獲得兩個(gè)鎖共享刀和共享叉
14、的鎖假如線程 "A" 獲得了刀而線程 "B" 獲得了叉線 程 A 就會(huì)進(jìn)入阻塞狀態(tài)來(lái)等待獲得叉而線程 B 則阻塞來(lái)等待 A 所擁有的刀這只是人為設(shè)計(jì)的例子但盡管在運(yùn)行時(shí)很難探測(cè)到這類情況卻時(shí)常發(fā)生雖然要探測(cè)或推敲各種情況是非常困難的 但只要按照下面幾條規(guī)則去設(shè)計(jì)系統(tǒng)就能夠避免死鎖問(wèn)題 讓所有的線程按照同樣的順序獲得一組鎖這種方法消除了 X 和 Y 的擁有者分別等待對(duì)方的資源的問(wèn)題 將多個(gè)鎖組成一組并放到同一個(gè)鎖下前面死鎖的例子中可以創(chuàng)建一個(gè)銀器對(duì)象的鎖于是在獲得刀或叉之前都必須獲得這個(gè)銀器的鎖 將那些不會(huì)阻塞的可獲得資源用變量標(biāo)志出來(lái)當(dāng)某個(gè)線程獲得銀器對(duì)
15、象的鎖時(shí)就可以通過(guò) 檢查變量來(lái)判斷是否整個(gè)銀器集合中的對(duì)象鎖都可獲得如果是它就可以獲得相關(guān)的鎖否則就要釋放掉銀器這個(gè)鎖并稍后再嘗試 最重要的是在編寫(xiě)代碼前認(rèn)真仔細(xì)地設(shè)計(jì)整個(gè)系統(tǒng)多線程是困難的在開(kāi)始編程之前詳細(xì) 設(shè)計(jì)系統(tǒng)能夠幫助你避免難以發(fā)現(xiàn)死鎖的問(wèn)題Volatile 變量.volatile關(guān)鍵字是 Java 語(yǔ)言為優(yōu)化編譯器設(shè)計(jì)的以下面的代碼為例 class VolatileTest public void foo( boolean flag = false; if(flag /this could happen 一個(gè)優(yōu)化的編譯器可能會(huì)判斷出 if 部分的語(yǔ)句永遠(yuǎn)不會(huì)被執(zhí)行 就根本不會(huì)編譯這
16、部分的代碼 如果這個(gè)類被多線程訪問(wèn) flag 被前面某個(gè)線程設(shè)置之后 在它被 if 語(yǔ) 句測(cè)試之前 可以被其他線程重新設(shè)置 用 volatile 關(guān)鍵字來(lái)聲明變量 就可以告訴 編譯器在編譯的時(shí)候 不需要通過(guò)預(yù)測(cè)變量值來(lái)優(yōu)化這部分的代碼 無(wú)法訪問(wèn)的線程 有時(shí)候雖然獲取對(duì)象鎖沒(méi)有問(wèn)題 IO 就是這類問(wèn)題最好的例子 訪問(wèn) 線程依然有可能進(jìn)入阻塞狀態(tài) 在 Java 編程中 當(dāng)線程因?yàn)閷?duì)象內(nèi)的 IO 調(diào)用而阻塞時(shí) 此對(duì)象應(yīng)當(dāng)仍能被其他線程 該對(duì)象通常有責(zé)任取消這個(gè)阻塞的 IO 操作 當(dāng)線程被阻塞時(shí) 造成阻塞調(diào)用的線程常常會(huì)令同步任務(wù)失敗 如 此對(duì)象也就相當(dāng)于被冷凍住了 其他的線程由于 果該對(duì)象的其他方法
17、也是同步的 不能獲得對(duì)象的鎖 就不能給此對(duì)象發(fā)消息 例如 取消 IO 操作 些阻塞調(diào)用 或確認(rèn)在一個(gè)用同步阻塞代碼的對(duì)象中存在非同步方法 必須確保不在同步代碼中包含那 盡管這種方法需要花費(fèi)一些注 意力來(lái)保證結(jié)果代碼安全運(yùn)行 但它允許在擁有對(duì)象的線程發(fā)生阻塞后 該對(duì)象仍能夠響應(yīng)其他線程 為不同的線程模型進(jìn)行設(shè)計(jì) 判斷是搶占式還是協(xié)作式的線程模型 取決于虛擬機(jī)的實(shí)現(xiàn)者 并根據(jù)各種實(shí)現(xiàn)而不同 因 此 Java 開(kāi)發(fā)員必須編寫(xiě)那些能夠在兩種模型上工作的程序 正如前面所提到的 在搶占式模型中線程可以在代碼的任何一個(gè)部分的中間被打斷 除非那 是一個(gè)原子操作代碼塊 原子操作代碼塊中的代碼段一旦開(kāi)始執(zhí)行 就要
18、在該線程被換出處 理器之前執(zhí)行完畢 在 Java 編程中 分配一個(gè)小于 32 位的變量空間是一種原子操作 而 此外象 double 和 long 這兩個(gè) 64 位數(shù)據(jù)類型的分配就不是原子的 使用鎖來(lái)正確同步共享 資源的訪問(wèn) 就足以保證一個(gè)多線程程序在搶占式模型下正確工作 而在協(xié)作式模型中 是否能保證線程正常放棄處理器 不掠奪其他線程的執(zhí)行時(shí)間 則完全 取決于程序員 調(diào)用 yield( 方法能夠?qū)?dāng)前的線程從處理器中移出到準(zhǔn)備就緒隊(duì)列中 另 一個(gè)方法則是調(diào)用 sleep( 方法 使線程放棄處理器 并且在 sleep 方法中指定的時(shí)間間隔 內(nèi)睡眠 正如你所想的那樣 將這些方法隨意放在代碼的某個(gè)地方
19、 并不能夠保證正常工作 如果線 程正擁有一個(gè)鎖 因?yàn)樗谝粋€(gè)同步方法或代碼塊中 則當(dāng)它調(diào)用 yield( 時(shí)不能夠釋放 這個(gè)鎖 這就意味著即使這個(gè)線程已經(jīng)被掛起 等待這個(gè)鎖釋放的其他線程依然不能繼續(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ú)法放棄
20、這些鎖 此外 一個(gè)因?yàn)檎{(diào)用 wait( 方法而阻塞的 線程 只有當(dāng)其他線程調(diào)用 notifyAll( 時(shí)才會(huì)被喚醒 線程和 AWT/Swing 在那些使用 Swing 和/或 AWT 包創(chuàng)建 GUI 用戶圖形界面 的 Java 程序中 AWT 事 件句柄在它自己的線程中運(yùn)行 開(kāi)發(fā)員必須注意避免將這些 GUI 線程與較耗時(shí)間的計(jì)算工 作綁在一起 因?yàn)檫@些線程必須負(fù)責(zé)處理用戶時(shí)間并重繪用戶圖形界面 換句話來(lái)說(shuō) 一旦 GUI 線程處于繁忙 整個(gè)程序看起來(lái)就象無(wú)響應(yīng)狀態(tài) Swing 線程通過(guò)調(diào)用合適方法 通 知那些 Swing callback 例如 Mouse Listener 和 Action L
21、istener 這種方法意味著 listener 無(wú)論要做多少事情 都應(yīng)當(dāng)利用 listener callback 方法產(chǎn)生其他線程來(lái)完成此項(xiàng)工 作 目的便在于讓 listener callback 更快速返回 從而允許 Swing 線程響應(yīng)其他事件 如果一個(gè) Swing 線程不能夠同步運(yùn)行 響應(yīng)事件并重繪輸出 那怎么能夠讓其他的線程安 全地修改 Swing 的狀態(tài) 正如上面提到的 Swing callback 在 Swing 線程中運(yùn)行 因此 他們能修改 Swing 數(shù)據(jù)并繪到屏幕上 但是如果不是 Swing callback 產(chǎn)生的變化該怎么辦呢 使用一個(gè)非 Swing 線程來(lái)修改 Swing 數(shù)據(jù)是不安全的 Swing 提供了兩個(gè)方法來(lái)解決這個(gè)問(wèn)題 invokeLater( 和 invokeAndWait( 為了修改 S
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 江西婺源茶業(yè)職業(yè)學(xué)院《造型基礎(chǔ)》2023-2024學(xué)年第一學(xué)期期末試卷
- 山西晉中理工學(xué)院《走進(jìn)》2023-2024學(xué)年第一學(xué)期期末試卷
- 華東理工大學(xué)《生存分析》2023-2024學(xué)年第一學(xué)期期末試卷
- 資陽(yáng)口腔職業(yè)學(xué)院《醫(yī)用微生物學(xué)》2023-2024學(xué)年第一學(xué)期期末試卷
- 2025至2030酒吧行業(yè)發(fā)展趨勢(shì)分析與未來(lái)投資戰(zhàn)略咨詢研究報(bào)告
- 安徽商貿(mào)職業(yè)技術(shù)學(xué)院《物流倉(cāng)儲(chǔ)與配送管理》2023-2024學(xué)年第一學(xué)期期末試卷
- 北京工業(yè)大學(xué)《運(yùn)動(dòng)處方原理》2023-2024學(xué)年第一學(xué)期期末試卷
- 哈爾濱科學(xué)技術(shù)職業(yè)學(xué)院《韓國(guó)語(yǔ)言與文化》2023-2024學(xué)年第一學(xué)期期末試卷
- 高管團(tuán)隊(duì)變化對(duì)企業(yè)戰(zhàn)略調(diào)整的影響分析
- 成都文理學(xué)院《口腔臨床醫(yī)學(xué)概要》2023-2024學(xué)年第一學(xué)期期末試卷
- 塔式起重機(jī)安裝驗(yàn)收牌
- 有機(jī)硅化學(xué)課件-有機(jī)硅化學(xué)基本反應(yīng)
- 如何根據(jù)三視圖畫(huà)軸測(cè)圖及補(bǔ)視圖缺線課件
- 《水產(chǎn)養(yǎng)殖前沿講座》課程教學(xué)大綱
- 漁業(yè)成品油價(jià)格補(bǔ)助專項(xiàng)資金管理暫行辦法
- 水庫(kù)工程建設(shè)征地移民安置監(jiān)測(cè)評(píng)估本底調(diào)查報(bào)告
- 2023年06月新疆生產(chǎn)建設(shè)兵團(tuán)第十二師“三支一扶”招募高校畢業(yè)生筆試題庫(kù)含答案解析
- 基于C#的WinForm程序設(shè)計(jì)學(xué)習(xí)通課后章節(jié)答案期末考試題庫(kù)2023年
- 十堰市張灣區(qū)紅衛(wèi)街道社區(qū)工作者考試真題2022
- 突發(fā)性耳聾培訓(xùn)課件
- DB65T 3558-2013多浪羊飼養(yǎng)管理技術(shù)規(guī)程
評(píng)論
0/150
提交評(píng)論