好東西分享
線程或者說(shuō)多線程,是我們處理多任務(wù)的強(qiáng)大工具。線程和進(jìn)程是不同的,每個(gè)進(jìn)程都是一個(gè)獨(dú)立運(yùn)行的程序,擁有自己的變量,且不同進(jìn)程間的變量不能共享;而線程是運(yùn)行在進(jìn)程內(nèi)部的,每個(gè)正在運(yùn)行的進(jìn)程至少有一個(gè)線程,而且不同的線程之間可以在進(jìn)程范圍內(nèi)共享數(shù)據(jù)。也就是說(shuō)進(jìn)程有自己獨(dú)立的存儲(chǔ)空間,而線程是和它所屬的進(jìn)程內(nèi)的其他線程共享一個(gè)存儲(chǔ)空間。線程的使用可以使我們能夠并行地處理一些事情。線程通過(guò)并行的處理給用戶帶來(lái)更好的使用體驗(yàn),比如你使用的郵件系統(tǒng)(outlook、Thunderbird、foxmail等),你當(dāng)然不希望它們?cè)谑杖⌒锣]件的時(shí)候,導(dǎo)致你連已經(jīng)收下來(lái)的郵件都無(wú)法閱讀,而只能等待收取郵件操作執(zhí)行完畢。這正是線程的意義所在。
實(shí)現(xiàn)線程的方式
實(shí)現(xiàn)線程的方式有兩種:
- 繼承java.lang.Thread,并重寫(xiě)它的run()方法,將線程的執(zhí)行主體放入其中。
- 實(shí)現(xiàn)java.lang.Runnable接口,實(shí)現(xiàn)它的run()方法,并將線程的執(zhí)行主體放入其中。
這是繼承Thread類(lèi)實(shí)現(xiàn)線程的示例:
- public ? class ?ThreadTest? extends ?Thread?{ ??
- ???? public ? void ?run()?{ ??
- ???????? //?在這里編寫(xiě)線程執(zhí)行的主體 ??
- ???????? //?do?something ??
- ????} ??
- }??
public class ThreadTest extends Thread { public void run() { // 在這里編寫(xiě)線程執(zhí)行的主體 // do something } }
這是實(shí)現(xiàn)Runnable接口實(shí)現(xiàn)多線程的示例:
- public ? class ?RunnableTest? implements ?Runnable?{ ??
- ???? public ? void ?run()?{ ??
- ???????? //?在這里編寫(xiě)線程執(zhí)行的主體 ??
- ???????? //?do?something ??
- ????} ??
- }??
public class RunnableTest implements Runnable { public void run() { // 在這里編寫(xiě)線程執(zhí)行的主體 // do something } }
這兩種實(shí)現(xiàn)方式的區(qū)別并不大。繼承Thread類(lèi)的方式實(shí)現(xiàn)起來(lái)較為簡(jiǎn)單,但是繼承它的類(lèi)就不能再繼承別的類(lèi)了,因此也就不能繼承別的類(lèi)的有用的方法了。而使用是想Runnable接口的方式就不存在這個(gè)問(wèn)題了,而且這種實(shí)現(xiàn)方式將線程主體和線程對(duì)象本身分離開(kāi)來(lái),邏輯上也較為清晰,所以推薦大家更多地采用這種方式。
如何啟動(dòng)線程
我們通過(guò)以上兩種方式實(shí)現(xiàn)了一個(gè)線程之后,線程的實(shí)例并沒(méi)有被創(chuàng)建,因此它們也并沒(méi)有被運(yùn)行。我們要啟動(dòng)一個(gè)線程,必須調(diào)用方法來(lái)啟動(dòng)它,這個(gè)方法就是Thread類(lèi)的start()方法,而不是run()方法(既不是我們繼承Thread類(lèi)重寫(xiě)的run()方法,也不是實(shí)現(xiàn)Runnable接口的run()方法)。run()方法中包含的是線程的主體,也就是這個(gè)線程被啟動(dòng)后將要運(yùn)行的代碼,它跟線程的啟動(dòng)沒(méi)有任何關(guān)系。上面兩種實(shí)現(xiàn)線程的方式在啟動(dòng)時(shí)會(huì)有所不同。
繼承Thread類(lèi)的啟動(dòng)方式:
- public ? class ?ThreadStartTest?{ ??
- ???? public ? static ? void ?main(String[]?args)?{ ??
- ???????? //?創(chuàng)建一個(gè)線程實(shí)例 ??
- ????????ThreadTest?tt?=? new ?ThreadTest(); ??
- ???????? //?啟動(dòng)線程 ??
- ????????tt.start(); ??
- ????} ??
- }??
public class ThreadStartTest { public static void main(String[] args) { // 創(chuàng)建一個(gè)線程實(shí)例 ThreadTest tt = new ThreadTest(); // 啟動(dòng)線程 tt.start(); } }
實(shí)現(xiàn)Runnable接口的啟動(dòng)方式:
- public ? class ?RunnableStartTest?{ ??
- ???? public ? static ? void ?main(String[]?args)?{ ??
- ???????? //?創(chuàng)建一個(gè)線程實(shí)例 ??
- ????????Thread?t?=? new ?Thread( new ?RunnableTest()); ??
- ???????? //?啟動(dòng)線程 ??
- ????????t.start(); ??
- ????} ??
- }??
public class RunnableStartTest { public static void main(String[] args) { // 創(chuàng)建一個(gè)線程實(shí)例 Thread t = new Thread(new RunnableTest()); // 啟動(dòng)線程 t.start(); } }
實(shí)際上這兩種啟動(dòng)線程的方式原理是一樣的。首先都是調(diào)用本地方法啟動(dòng)一個(gè)線程,其次是在這個(gè)線程里執(zhí)行目標(biāo)對(duì)象的run()方法。那么這個(gè)目標(biāo)對(duì)象是什么呢?為了弄明白這個(gè)問(wèn)題,我們來(lái)看看Thread類(lèi)的run()方法的實(shí)現(xiàn):
- public ? void ?run()?{ ??
- ???? if ?(target?!=? null )?{ ??
- ????????target.run(); ??
- ????} ??
- }??
public void run() { if (target != null) { target.run(); } }
當(dāng)我們采用實(shí)現(xiàn)Runnable接口的方式來(lái)實(shí)現(xiàn)線程的情況下,在調(diào)用new Thread(Runnable target)構(gòu)造器時(shí),將實(shí)現(xiàn)Runnable接口的類(lèi)的實(shí)例設(shè)置成了線程要執(zhí)行的主體所屬的目標(biāo)對(duì)象target,當(dāng)線程啟動(dòng)時(shí),這個(gè)實(shí)例的run()方法就被執(zhí)行了。當(dāng)我們采用繼承Thread的方式實(shí)現(xiàn)線程時(shí),線程的這個(gè)run()方法被重寫(xiě)了,所以當(dāng)線程啟動(dòng)時(shí),執(zhí)行的是這個(gè)對(duì)象自身的run()方法。總結(jié)起來(lái)就一句話,線程類(lèi)有一個(gè)Runnable類(lèi)型的target屬性,它是線程啟動(dòng)后要執(zhí)行的run()方法所屬的主體,如果我們采用的是繼承Thread類(lèi)的方式,那么這個(gè)target就是線程對(duì)象自身,如果我們采用的是實(shí)現(xiàn)Runnable接口的方式,那么這個(gè)target就是實(shí)現(xiàn)了Runnable接口的類(lèi)的實(shí)例。
線程的狀態(tài)
在Java 1.4及以下的版本中,每個(gè)線程都具有新建、可運(yùn)行、阻塞、死亡四種狀態(tài),但是在Java 5.0及以上版本中,線程的狀態(tài)被擴(kuò)充為新建、可運(yùn)行、阻塞、等待、定時(shí)等待、死亡六種。線程的狀態(tài)完全包含了一個(gè)線程從新建到運(yùn)行,最后到結(jié)束的整個(gè)生命周期。線程狀態(tài)的具體信息如下:
- NEW(新建狀態(tài)、初始化狀態(tài)): 線程對(duì)象已經(jīng)被創(chuàng)建,但是還沒(méi)有被啟動(dòng)時(shí)的狀態(tài)。這段時(shí)間就是在我們調(diào)用new命令之后,調(diào)用start()方法之前。
- RUNNABLE(可運(yùn)行狀態(tài)、就緒狀態(tài)): 在我們調(diào)用了線程的start()方法之后線程所處的狀態(tài)。處于RUNNABLE狀態(tài)的線程在JAVA虛擬機(jī)(JVM)上是運(yùn)行著的,但是它可能還正在等待操作系統(tǒng)分配給它相應(yīng)的運(yùn)行資源以得以運(yùn)行。
- BLOCKED(阻塞狀態(tài)、被中斷運(yùn)行): 線程正在等待其它的線程釋放同步鎖,以進(jìn)入一個(gè)同步塊或者同步方法繼續(xù)運(yùn)行;或者它已經(jīng)進(jìn)入了某個(gè)同步塊或同步方法,在運(yùn)行的過(guò)程中它調(diào)用了某個(gè)對(duì)象繼承自java.lang.Object的wait()方法,正在等待重新返回這個(gè)同步塊或同步方法。
- WAITING(等待狀態(tài)): 當(dāng)前線程調(diào)用了java.lang.Object.wait()、java.lang.Thread.join()或者java.util.concurrent.locks.LockSupport.park()三個(gè)中的任意一個(gè)方法,正在等待另外一個(gè)線程執(zhí)行某個(gè)操作。比如一個(gè)線程調(diào)用了某個(gè)對(duì)象的wait()方法,正在等待其它線程調(diào)用這個(gè)對(duì)象的notify()或者notifyAll()(這兩個(gè)方法同樣是繼承自O(shè)bject類(lèi))方法來(lái)喚醒它;或者一個(gè)線程調(diào)用了另一個(gè)線程的join()(這個(gè)方法屬于Thread類(lèi))方法,正在等待這個(gè)方法運(yùn)行結(jié)束。
- TIMED_WAITING(定時(shí)等待狀態(tài)): 當(dāng)前線程調(diào)用了java.lang.Object.wait(long timeout)、java.lang.Thread.join(long millis)、java.util.concurrent.locks.LockSupport.packNanos(long nanos)、java.util.concurrent.locks.LockSupport.packUntil(long deadline)四個(gè)方法中的任意一個(gè),進(jìn)入等待狀態(tài),但是與WAITING狀態(tài)不同的是,它有一個(gè)最大等待時(shí)間,即使等待的條件仍然沒(méi)有滿足,只要到了這個(gè)時(shí)間它就會(huì)自動(dòng)醒來(lái)。
- TERMINATED(死亡狀態(tài)、終止?fàn)顟B(tài)): 線程完成執(zhí)行后的狀態(tài)。線程執(zhí)行完run()方法中的全部代碼,從該方法中退出,進(jìn)入TERMINATED狀態(tài)。還有一種情況是run()在運(yùn)行過(guò)程中拋出了一個(gè)異常,而這個(gè)異常沒(méi)有被程序捕獲,導(dǎo)致這個(gè)線程異常終止進(jìn)入TERMINATED狀態(tài)。
在Java5.0及以上版本中,線程的全部六種狀態(tài)都以枚舉類(lèi)型的形式定義在java.lang.Thread類(lèi)中了,代碼如下:
- public ? enum ?State?{ ??
- ????NEW, ??
- ????RUNNABLE, ??
- ????BLOCKED, ??
- ????WAITING, ??
- ????TIMED_WAITING, ??
- ????TERMINATED; ??
- }??
public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
sleep()和wait()的區(qū)別
sleep()方法和wait()方法都成產(chǎn)生讓當(dāng)前運(yùn)行的線程停止運(yùn)行的效果,這是它們的共同點(diǎn)。下面我們來(lái)詳細(xì)說(shuō)說(shuō)它們的不同之處。
sleep()方法是本地方法,屬于Thread類(lèi),它有兩種定義:
- public ? static ? native ? void ?sleep( long ?millis)? throws ?InterruptedException; ??
- ??
- public ? static ? void ?sleep( long ?millis,? int ?nanos)? throws ?InterruptedException?{ ??
- ???? //other?code ??
- }??
public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos) throws InterruptedException { //other code }
其中的參數(shù)millis代表毫秒數(shù)(千分之一秒),nanos代表納秒數(shù)(十億分之一秒)。這兩個(gè)方法都可以讓調(diào)用它的線程沉睡(停止運(yùn)行)指定的時(shí)間,到了這個(gè)時(shí)間,線程就會(huì)自動(dòng)醒來(lái),變?yōu)榭蛇\(yùn)行狀態(tài)(RUNNABLE),但這并不表示它馬上就會(huì)被運(yùn)行,因?yàn)榫€程調(diào)度機(jī)制恢復(fù)線程的運(yùn)行也需要時(shí)間。調(diào)用sleep()方法并不會(huì)讓線程釋放它所持有的同步鎖;而且在這期間它也不會(huì)阻礙其它線程的運(yùn)行。上面的連個(gè)方法都聲明拋出一個(gè)InterruptedException類(lèi)型的異常,這是因?yàn)榫€程在sleep()期間,有可能被持有它的引用的其它線程調(diào)用它的interrupt()方法而中斷。中斷一個(gè)線程會(huì)導(dǎo)致一個(gè)InterruptedException異常的產(chǎn)生,如果你的程序不捕獲這個(gè)異常,線程就會(huì)異常終止,進(jìn)入TERMINATED狀態(tài),如果你的程序捕獲了這個(gè)異常,那么程序就會(huì)繼續(xù)執(zhí)行catch語(yǔ)句塊(可能還有finally語(yǔ)句塊)以及以后的代碼。
為了更好地理解interrupt()效果,我們來(lái)看一下下面這個(gè)例子:
- public ? class ?InterruptTest?{ ??
- ???? public ? static ? void ?main(String[]?args)?{ ??
- ????????Thread?t?=? new ?Thread()?{ ??
- ???????????? public ? void ?run()?{ ??
- ???????????????? try ?{ ??
- ????????????????????System.out.println( "我被執(zhí)行了-在sleep()方法前" ); ??
- ???????????????????? //?停止運(yùn)行10分鐘 ??
- ????????????????????Thread.sleep( 1000 ?*? 60 ?*? 10 ); ??
- ????????????????????System.out.println( "我被執(zhí)行了-在sleep()方法后" ); ??
- ????????????????}? catch ?(InterruptedException?e)?{ ??
- ????????????????????System.out.println( "我被執(zhí)行了-在catch語(yǔ)句塊中" ); ??
- ????????????????} ??
- ????????????????System.out.println( "我被執(zhí)行了-在try{}語(yǔ)句塊后" ); ??
- ????????????} ??
- ????????}; ??
- ???????? //?啟動(dòng)線程 ??
- ????????t.start(); ??
- ???????? //?在sleep()結(jié)束前中斷它 ??
- ????????t.interrupt(); ??
- ????} ??
- }??
public class InterruptTest { public static void main(String[] args) { Thread t = new Thread() { public void run() { try { System.out.println("我被執(zhí)行了-在sleep()方法前"); // 停止運(yùn)行10分鐘 Thread.sleep(1000 * 60 * 10); System.out.println("我被執(zhí)行了-在sleep()方法后"); } catch (InterruptedException e) { System.out.println("我被執(zhí)行了-在catch語(yǔ)句塊中"); } System.out.println("我被執(zhí)行了-在try{}語(yǔ)句塊后"); } }; // 啟動(dòng)線程 t.start(); // 在sleep()結(jié)束前中斷它 t.interrupt(); } }
運(yùn)行結(jié)果:
- 我被執(zhí)行了-在sleep()方法前
- 我被執(zhí)行了-在catch語(yǔ)句塊中
- 我被執(zhí)行了-在try{}語(yǔ)句塊后
wait()方法也是本地方法,屬于Object類(lèi),有三個(gè)定義:
- public ? final ? void ?wait()? throws ?InterruptedException?{ ??
- ???? //do?something ??
- } ??
- ??
- public ? final ? native ? void ?wait( long ?timeout)? throws ?InterruptedException; ??
- ??
- public ? final ? void ?wait( long ?timeout,? int ?nanos)? throws ?InterruptedException?{ ??
- ???? //do?something ??
- }??
public final void wait() throws InterruptedException { //do something } public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { //do something }
wari()和wait(long timeout,int nanos)方法都是基于wait(long timeout)方法實(shí)現(xiàn)的。同樣地,timeout代表毫秒數(shù),nanos代表納秒數(shù)。當(dāng)調(diào)用了某個(gè)對(duì)象的wait()方法時(shí),當(dāng)前運(yùn)行的線程就會(huì)轉(zhuǎn)入等待狀態(tài)(WAITING),等待別的線程再次調(diào)用這個(gè)對(duì)象的notify()或者notifyAll()方法(這兩個(gè)方法也是本地方法)喚醒它,或者到了指定的最大等待時(shí)間,線程自動(dòng)醒來(lái)。如果線程擁有某個(gè)或某些對(duì)象的同步鎖,那么在調(diào)用了wait()后,這個(gè)線程就會(huì)釋放它持有的所有同步資源,而不限于這個(gè)被調(diào)用了wait()方法的對(duì)象。wait()方法同樣會(huì)被Thread類(lèi)的interrupt()方法中斷,并產(chǎn)生一個(gè)InterruptedException異常,效果同sleep()方法被中斷一樣。
實(shí)現(xiàn)同步的方式
同步是多線程中的重要概念。同步的使用可以保證在多線程運(yùn)行的環(huán)境中,程序不會(huì)產(chǎn)生設(shè)計(jì)之外的錯(cuò)誤結(jié)果。同步的實(shí)現(xiàn)方式有兩種,同步方法和同步塊,這兩種方式都要用到synchronized關(guān)鍵字。
給一個(gè)方法增加synchronized修飾符之后就可以使它成為同步方法,這個(gè)方法可以是靜態(tài)方法和非靜態(tài)方法,但是不能是抽象類(lèi)的抽象方法,也不能是接口中的接口方法。下面代碼是一個(gè)同步方法的示例:
- public ? synchronized ? void ?aMethod()?{ ??
- ???? //?do?something ??
- } ??
- ??
- public ? static ? synchronized ? void ?anotherMethod()?{ ??
- ???? //?do?something ??
- }??
public synchronized void aMethod() { // do something } public static synchronized void anotherMethod() { // do something }
線程在執(zhí)行同步方法時(shí)是具有排它性的。當(dāng)任意一個(gè)線程進(jìn)入到一個(gè)對(duì)象的任意一個(gè)同步方法時(shí),這個(gè)對(duì)象的所有同步方法都被鎖定了,在此期間,其他任何線程都不能訪問(wèn)這個(gè)對(duì)象的任意一個(gè)同步方法,直到這個(gè)線程執(zhí)行完它所調(diào)用的同步方法并從中退出,從而導(dǎo)致它釋放了該對(duì)象的同步鎖之后。在一個(gè)對(duì)象被某個(gè)線程鎖定之后,其他線程是可以訪問(wèn)這個(gè)對(duì)象的所有非同步方法的。
同步塊的形式雖然與同步方法不同,但是原理和效果是一致的。同步塊是通過(guò)鎖定一個(gè)指定的對(duì)象,來(lái)對(duì)同步塊中包含的代碼進(jìn)行同步;而同步方法是對(duì)這個(gè)方法塊里的代碼進(jìn)行同步,而這種情況下鎖定的對(duì)象就是同步方法所屬的主體對(duì)象自身。如果這個(gè)方法是靜態(tài)同步方法呢?那么線程鎖定的就不是這個(gè)類(lèi)的對(duì)象了,也不是這個(gè)類(lèi)自身,而是這個(gè)類(lèi)對(duì)應(yīng)的java.lang.Class類(lèi)型的對(duì)象。同步方法和同步塊之間的相互制約只限于同一個(gè)對(duì)象之間,所以靜態(tài)同步方法只受它所屬類(lèi)的其它靜態(tài)同步方法的制約,而跟這個(gè)類(lèi)的實(shí)例(對(duì)象)沒(méi)有關(guān)系。
下面這段代碼演示了同步塊的實(shí)現(xiàn)方式:
- public ? void ?test()?{ ??
- ???? //?同步鎖 ??
- ????String?lock?=? "LOCK" ; ??
- ??
- ???? //?同步塊 ??
- ???? synchronized ?(lock)?{ ??
- ???????? //?do?something ??
- ????} ??
- ??
- ???? int ?i?=? 0 ; ??
- ???? //?... ??
- }??
public void test() { // 同步鎖 String lock = "LOCK"; // 同步塊 synchronized (lock) { // do something } int i = 0; // ... }
對(duì)于作為同步鎖的對(duì)象并沒(méi)有什么特別要求,任意一個(gè)對(duì)象都可以。如果一個(gè)對(duì)象既有同步方法,又有同步塊,那么當(dāng)其中任意一個(gè)同步方法或者同步塊被某個(gè)線程執(zhí)行時(shí),這個(gè)對(duì)象就被鎖定了,其他線程無(wú)法在此時(shí)訪問(wèn)這個(gè)對(duì)象的同步方法,也不能執(zhí)行同步塊。
synchronized和Lock
Lock是一個(gè)接口,它位于Java 5.0新增的java.utils.concurrent包的子包locks中。concurrent包及其子包中的類(lèi)都是用來(lái)處理多線程編程的。實(shí)現(xiàn)Lock接口的類(lèi)具有與synchronized關(guān)鍵字同樣的功能,但是它更加強(qiáng)大一些。java.utils.concurrent.locks.ReentrantLock是較常用的實(shí)現(xiàn)了Lock接口的類(lèi)。下面是ReentrantLock類(lèi)的一個(gè)應(yīng)用實(shí)例:
- private ?Lock?lock?=? new ?ReentrantLock(); ??
- ??
- public ? void ?testLock()?{ ??
- ???? //?鎖定對(duì)象 ??
- ????lock.lock(); ??
- ???? try ?{ ??
- ???????? //?do?something ??
- ????}? finally ?{ ??
- ???????? //?釋放對(duì)對(duì)象的鎖定 ??
- ????????lock.unlock(); ??
- ????} ??
- }??
private Lock lock = new ReentrantLock(); public void testLock() { // 鎖定對(duì)象 lock.lock(); try { // do something } finally { // 釋放對(duì)對(duì)象的鎖定 lock.unlock(); } }
lock()方法用于鎖定對(duì)象,unlock()方法用于釋放對(duì)對(duì)象的鎖定,他們都是在Lock接口中定義的方法。位于這兩個(gè)方法之間的代碼在被執(zhí)行時(shí),效果等同于被放在synchronized同步塊中。一般用法是將需要在lock()和unlock()方法之間執(zhí)行的代碼放在try{}塊中,并且在finally{}塊中調(diào)用unlock()方法,這樣就可以保證即使在執(zhí)行代碼拋出異常的情況下,對(duì)象的鎖也總是會(huì)被釋放,否則的話就會(huì)為死鎖的產(chǎn)生增加可能。
使用synchronized關(guān)鍵字實(shí)現(xiàn)的同步,會(huì)把一個(gè)對(duì)象的所有同步方法和同步塊看做一個(gè)整體,只要有一個(gè)被某個(gè)線程調(diào)用了,其他的就無(wú)法被別的線程執(zhí)行,即使這些方法或同步塊與被調(diào)用的代碼之間沒(méi)有任何邏輯關(guān)系,這顯然降低了程序的運(yùn)行效率。而使用Lock就能夠很好地解決這個(gè)問(wèn)題。我們可以把一個(gè)對(duì)象中按照邏輯關(guān)系把需要同步的方法或代碼進(jìn)行分組,為每個(gè)組創(chuàng)建一個(gè)Lock類(lèi)型的對(duì)象,對(duì)實(shí)現(xiàn)同步。那么,當(dāng)一個(gè)同步塊被執(zhí)行時(shí),這個(gè)線程只會(huì)鎖定與當(dāng)前運(yùn)行代碼相關(guān)的其他代碼最小集合,而并不影響其他線程對(duì)其余同步代碼的調(diào)用執(zhí)行。
關(guān)于死鎖
死鎖就是一個(gè)進(jìn)程中的每個(gè)線程都在等待這個(gè)進(jìn)程中的其他線程釋放所占用的資源,從而導(dǎo)致所有線程都無(wú)法繼續(xù)執(zhí)行的情況。死鎖是多線程編程中一個(gè)隱藏的陷阱,它經(jīng)常發(fā)生在多個(gè)線程共用資源的時(shí)候。在實(shí)際開(kāi)發(fā)中,死鎖一般隱藏的較深,不容易被發(fā)現(xiàn),一旦死鎖現(xiàn)象發(fā)生,就必然會(huì)導(dǎo)致程序的癱瘓。因此必須避免它的發(fā)生。
程序中必須同時(shí)滿足以下四個(gè)條件才會(huì)引發(fā)死鎖:
- 互斥(Mutual exclusion): 線程所使用的資源中至少有一個(gè)是不能共享的,它在同一時(shí)刻只能由一個(gè)線程使用。
- 持有與等待(Hold and wait): 至少有一個(gè)線程已經(jīng)持有了資源,并且正在等待獲取其他的線程所持有的資源。
- 非搶占式(No pre-emption): 如果一個(gè)線程已經(jīng)持有了某個(gè)資源,那么在這個(gè)線程釋放這個(gè)資源之前,別的線程不能把它搶奪過(guò)去使用。
- 循環(huán)等待(Circular wait): 假設(shè)有N個(gè)線程在運(yùn)行,第一個(gè)線程持有了一個(gè)資源,并且正在等待獲取第二個(gè)線程持有的資源,而第二個(gè)線程正在等待獲取第三個(gè)線程持有的資源,依此類(lèi)推……第N個(gè)線程正在等待獲取第一個(gè)線程持有的資源,由此形成一個(gè)循環(huán)等待。
線程池
線程池就像數(shù)據(jù)庫(kù)連接池一樣,是一個(gè)對(duì)象池。所有的對(duì)象池都有一個(gè)共同的目的,那就是為了提高對(duì)象的使用率,從而達(dá)到提高程序效率的目的。比如對(duì)于Servlet,它被設(shè)計(jì)為多線程的(如果它是單線程的,你就可以想象,當(dāng)1000個(gè)人同時(shí)請(qǐng)求一個(gè)網(wǎng)頁(yè)時(shí),在第一個(gè)人獲得請(qǐng)求結(jié)果之前,其它999個(gè)人都在郁悶地等待),如果為每個(gè)用戶的每一次請(qǐng)求都創(chuàng)建一個(gè)新的線程對(duì)象來(lái)運(yùn)行的話,系統(tǒng)就會(huì)在創(chuàng)建線程和銷(xiāo)毀線程上耗費(fèi)很大的開(kāi)銷(xiāo),大大降低系統(tǒng)的效率。因此,Servlet多線程機(jī)制背后有一個(gè)線程池在支持,線程池在初始化初期就創(chuàng)建了一定數(shù)量的線程對(duì)象,通過(guò)提高對(duì)這些對(duì)象的利用率,避免高頻率地創(chuàng)建對(duì)象,從而達(dá)到提高程序的效率的目的。
下面實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的線程池,從中理解它的實(shí)現(xiàn)原理。為此我們定義了四個(gè)類(lèi),它們的用途及具體實(shí)現(xiàn)如下:
[list=1]
Task的定義如下:
- public ? abstract ? class ?Task?{ ??
- ???? public ? enum ?State?{ ??
- ???????? /*?新建?*/ NEW,? /*?執(zhí)行中?*/ RUNNING,? /*?已完成?*/ FINISHED ??
- ????} ??
- ??
- ???? //?任務(wù)狀態(tài) ??
- ???? private ?State?state?=?State.NEW; ??
- ??
- ???? public ? void ?setState(State?state)?{ ??
- ???????? this .state?=?state; ??
- ????} ??
- ??
- ???? public ?State?getState()?{ ??
- ???????? return ?state; ??
- ????} ??
- ??
- ???? public ? abstract ? void ?deal(); ??
- }??
public abstract class Task { public enum State { /* 新建 */NEW, /* 執(zhí)行中 */RUNNING, /* 已完成 */FINISHED } // 任務(wù)狀態(tài) private State state = State.NEW; public void setState(State state) { this.state = state; } public State getState() { return state; } public abstract void deal(); }
TaskQueue類(lèi)的定義如下:
- import ?java.util.Iterator; ??
- import ?java.util.LinkedList; ??
- import ?java.util.List; ??
- ??
- public ? class ?TaskQueue?{ ??
- ???? private ?List<Task>?queue?=? new ?LinkedList<Task>(); ??
- ??
- ???? //?添加一項(xiàng)任務(wù) ??
- ???? public ? synchronized ? void ?addTask(Task?task)?{ ??
- ???????? if ?(task?!=? null )?{ ??
- ????????????queue.add(task); ??
- ????????} ??
- ????} ??
- ??
- ???? //?完成任務(wù)后將它從任務(wù)隊(duì)列中刪除 ??
- ???? public ? synchronized ? void ?finishTask(Task?task)?{ ??
- ???????? if ?(task?!=? null )?{ ??
- ????????????task.setState(Task.State.FINISHED); ??
- ????????????queue.remove(task); ??
- ????????} ??
- ????} ??
- ??
- ???? //?取得一項(xiàng)待執(zhí)行任務(wù) ??
- ???? public ? synchronized ?Task?getTask()?{ ??
- ????????Iterator<Task>?it?=?queue.iterator(); ??
- ????????Task?task; ??
- ???????? while ?(it.hasNext())?{ ??
- ????????????task?=?it.next(); ??
- ???????????? //?尋找一個(gè)新建的任務(wù) ??
- ???????????? if ?(Task.State.NEW.equals(task.getState()))?{ ??
- ???????????????? //?把任務(wù)狀態(tài)置為運(yùn)行中 ??
- ????????????????task.setState(Task.State.RUNNING); ??
- ???????????????? return ?task; ??
- ????????????} ??
- ????????} ??
- ???????? return ? null ; ??
- ????} ??
- }??
import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class TaskQueue { private List<Task> queue = new LinkedList<Task>(); // 添加一項(xiàng)任務(wù) public synchronized void addTask(Task task) { if (task != null) { queue.add(task); } } // 完成任務(wù)后將它從任務(wù)隊(duì)列中刪除 public synchronized void finishTask(Task task) { if (task != null) { task.setState(Task.State.FINISHED); queue.remove(task); } } // 取得一項(xiàng)待執(zhí)行任務(wù) public synchronized Task getTask() { Iterator<Task> it = queue.iterator(); Task task; while (it.hasNext()) { task = it.next(); // 尋找一個(gè)新建的任務(wù) if (Task.State.NEW.equals(task.getState())) { // 把任務(wù)狀態(tài)置為運(yùn)行中 task.setState(Task.State.RUNNING); return task; } } return null; } }
addTask(Task task)方法用于當(dāng)一個(gè)新的任務(wù)到達(dá)時(shí),將它添加到任務(wù)隊(duì)列中。這里使用了LinkedList類(lèi)來(lái)保存任務(wù)到達(dá)的先后順序。finishTask(Task task)方法用于任務(wù)被執(zhí)行完畢時(shí),將它從任務(wù)隊(duì)列中清除出去。getTask()方法用于取得當(dāng)前要執(zhí)行的任務(wù)。
- public ? class ?TaskThread? extends ?Thread?{ ??
- ???? //?該線程所屬的線程池 ??
- ???? private ?ThreadPoolService?service; ??
- ??
- ???? public ?TaskThread(ThreadPoolService?tps)?{ ??
- ????????service?=?tps; ??
- ????} ??
- ??
- ???? public ? void ?run()?{ ??
- ???????? //?在線程池運(yùn)行的狀態(tài)下執(zhí)行任務(wù)隊(duì)列中的任務(wù) ??
- ???????? while ?(service.isRunning())?{ ??
- ????????????TaskQueue?queue?=?service.getTaskQueue(); ??
- ????????????Task?task?=?queue.getTask(); ??
- ???????????? if ?(task?!=? null )?{ ??
- ????????????????task.deal(); ??
- ????????????} ??
- ????????????queue.finishTask(task); ??
- ????????} ??
- ????} ??
- }??
public class TaskThread extends Thread { // 該線程所屬的線程池 private ThreadPoolService service; public TaskThread(ThreadPoolService tps) { service = tps; } public void run() { // 在線程池運(yùn)行的狀態(tài)下執(zhí)行任務(wù)隊(duì)列中的任務(wù) while (service.isRunning()) { TaskQueue queue = service.getTaskQueue(); Task task = queue.getTask(); if (task != null) { task.deal(); } queue.finishTask(task); } } }
ThreadPoolService類(lèi)的定義如下:
- import ?java.util.ArrayList; ??
- import ?java.util.List; ??
- ??
- public ? class ?ThreadPoolService?{ ??
- ???? //?線程數(shù) ??
- ???? public ? static ? final ? int ?THREAD_COUNT?=? 5 ; ??
- ??
- ???? //?線程池狀態(tài) ??
- ???? private ?Status?status?=?Status.NEW; ??
- ??
- ???? private ?TaskQueue?queue?=? new ?TaskQueue(); ??
- ??
- ???? public ? enum ?Status?{ ??
- ???????? /*?新建?*/ NEW,? /*?提供服務(wù)中?*/ RUNNING,? /*?停止服務(wù)?*/ TERMINATED, ??
- ????} ??
- ??
- ???? private ?List<Thread>?threads?=? new ?ArrayList<Thread>(); ??
- ??
- ???? public ?ThreadPoolService()?{ ??
- ???????? for ?( int ?i?=? 0 ;?i?<?THREAD_COUNT;?i++)?{ ??
- ????????????Thread?t?=? new ?TaskThread( this ); ??
- ????????????threads.add(t); ??
- ????????} ??
- ????} ??
- ??
- ???? //?啟動(dòng)服務(wù) ??
- ???? public ? void ?start()?{ ??
- ???????? this .status?=?Status.RUNNING; ??
- ???????? for ?( int ?i?=? 0 ;?i?<?THREAD_COUNT;?i++)?{ ??
- ????????????threads.get(i).start(); ??
- ????????} ??
- ????} ??
- ??
- ???? //?停止服務(wù) ??
- ???? public ? void ?stop()?{ ??
- ???????? this .status?=?Status.TERMINATED; ??
- ????} ??
- ??
- ???? //?是否正在運(yùn)行 ??
- ???? public ? boolean ?isRunning()?{ ??
- ???????? return ?status?==?Status.RUNNING; ??
- ????} ??
- ??
- ???? //?執(zhí)行任務(wù) ??
- ???? public ? void ?runTask(Task?task)?{ ??
- ????????queue.addTask(task); ??
- ????} ??
- ??
- ???? protected ?TaskQueue?getTaskQueue()?{ ??
- ???????? return ?queue; ??
- ????} ??
- }??
import java.util.ArrayList; import java.util.List; public class ThreadPoolService { // 線程數(shù) public static final int THREAD_COUNT = 5; // 線程池狀態(tài) private Status status = Status.NEW; private TaskQueue queue = new TaskQueue(); public enum Status { /* 新建 */NEW, /* 提供服務(wù)中 */RUNNING, /* 停止服務(wù) */TERMINATED, } private List<Thread> threads = new ArrayList<Thread>(); public ThreadPoolService() { for (int i = 0; i < THREAD_COUNT; i++) { Thread t = new TaskThread(this); threads.add(t); } } // 啟動(dòng)服務(wù) public void start() { this.status = Status.RUNNING; for (int i = 0; i < THREAD_COUNT; i++) { threads.get(i).start(); } } // 停止服務(wù) public void stop() { this.status = Status.TERMINATED; } // 是否正在運(yùn)行 public boolean isRunning() { return status == Status.RUNNING; } // 執(zhí)行任務(wù) public void runTask(Task task) { queue.addTask(task); } protected TaskQueue getTaskQueue() { return queue; } }
[/list]
完成了上面四個(gè)類(lèi),我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的線程池。現(xiàn)在我們就可以使用它了,下面的代碼做了一個(gè)簡(jiǎn)單的示例:
- public ? class ?SimpleTaskTest? extends ?Task?{ ??
- ???? @Override ??
- ???? public ? void ?deal()?{ ??
- ???????? //?do?something ??
- ????} ??
- ??
- ???? public ? static ? void ?main(String[]?args)? throws ?InterruptedException?{ ??
- ????????ThreadPoolService?service?=? new ?ThreadPoolService(); ??
- ????????service.start(); ??
- ???????? //?執(zhí)行十次任務(wù) ??
- ???????? for ?(</spa
- 2010-05-01 00:11
- 瀏覽 229
- 評(píng)論(0)
- 分類(lèi): 非技術(shù)
- 相關(guān)推薦
發(fā)表評(píng)論
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

評(píng)論