欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

JAVA面試題解惑系列(十)——話說(shuō)多線程

系統(tǒng) 2093 0

好東西分享

線程或者說(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)線程的方式有兩種:

  1. 繼承java.lang.Thread,并重寫(xiě)它的run()方法,將線程的執(zhí)行主體放入其中。
  2. 實(shí)現(xiàn)java.lang.Runnable接口,實(shí)現(xiàn)它的run()方法,并將線程的執(zhí)行主體放入其中。


這是繼承Thread類(lèi)實(shí)現(xiàn)線程的示例:

Java代碼 復(fù)制代碼
  1. public ? class ?ThreadTest? extends ?Thread?{ ??
  2. ???? public ? void ?run()?{ ??
  3. ???????? //?在這里編寫(xiě)線程執(zhí)行的主體 ??
  4. ???????? //?do?something ??
  5. ????} ??
  6. }??
    public class ThreadTest extends Thread {
	public void run() {
		// 在這里編寫(xiě)線程執(zhí)行的主體
		// do something
	}
}

  


這是實(shí)現(xiàn)Runnable接口實(shí)現(xiàn)多線程的示例:

Java代碼 復(fù)制代碼
  1. public ? class ?RunnableTest? implements ?Runnable?{ ??
  2. ???? public ? void ?run()?{ ??
  3. ???????? //?在這里編寫(xiě)線程執(zhí)行的主體 ??
  4. ???????? //?do?something ??
  5. ????} ??
  6. }??
    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)方式:

Java代碼 復(fù)制代碼
  1. public ? class ?ThreadStartTest?{ ??
  2. ???? public ? static ? void ?main(String[]?args)?{ ??
  3. ???????? //?創(chuàng)建一個(gè)線程實(shí)例 ??
  4. ????????ThreadTest?tt?=? new ?ThreadTest(); ??
  5. ???????? //?啟動(dòng)線程 ??
  6. ????????tt.start(); ??
  7. ????} ??
  8. }??
    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)方式:

Java代碼 復(fù)制代碼
  1. public ? class ?RunnableStartTest?{ ??
  2. ???? public ? static ? void ?main(String[]?args)?{ ??
  3. ???????? //?創(chuàng)建一個(gè)線程實(shí)例 ??
  4. ????????Thread?t?=? new ?Thread( new ?RunnableTest()); ??
  5. ???????? //?啟動(dòng)線程 ??
  6. ????????t.start(); ??
  7. ????} ??
  8. }??
    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):

Java代碼 復(fù)制代碼
  1. public ? void ?run()?{ ??
  2. ???? if ?(target?!=? null )?{ ??
  3. ????????target.run(); ??
  4. ????} ??
  5. }??
    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)的具體信息如下:

  1. NEW(新建狀態(tài)、初始化狀態(tài)): 線程對(duì)象已經(jīng)被創(chuàng)建,但是還沒(méi)有被啟動(dòng)時(shí)的狀態(tài)。這段時(shí)間就是在我們調(diào)用new命令之后,調(diào)用start()方法之前。
  2. 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)行。
  3. 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è)同步塊或同步方法。
  4. 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é)束。
  5. 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)。
  6. 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)中了,代碼如下:

Java代碼 復(fù)制代碼
  1. public ? enum ?State?{ ??
  2. ????NEW, ??
  3. ????RUNNABLE, ??
  4. ????BLOCKED, ??
  5. ????WAITING, ??
  6. ????TIMED_WAITING, ??
  7. ????TERMINATED; ??
  8. }??
    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),它有兩種定義:

Java代碼 復(fù)制代碼
  1. public ? static ? native ? void ?sleep( long ?millis)? throws ?InterruptedException; ??
  2. ??
  3. public ? static ? void ?sleep( long ?millis,? int ?nanos)? throws ?InterruptedException?{ ??
  4. ???? //other?code ??
  5. }??
    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è)例子:

Java代碼 復(fù)制代碼
  1. public ? class ?InterruptTest?{ ??
  2. ???? public ? static ? void ?main(String[]?args)?{ ??
  3. ????????Thread?t?=? new ?Thread()?{ ??
  4. ???????????? public ? void ?run()?{ ??
  5. ???????????????? try ?{ ??
  6. ????????????????????System.out.println( "我被執(zhí)行了-在sleep()方法前" ); ??
  7. ???????????????????? //?停止運(yùn)行10分鐘 ??
  8. ????????????????????Thread.sleep( 1000 ?*? 60 ?*? 10 ); ??
  9. ????????????????????System.out.println( "我被執(zhí)行了-在sleep()方法后" ); ??
  10. ????????????????}? catch ?(InterruptedException?e)?{ ??
  11. ????????????????????System.out.println( "我被執(zhí)行了-在catch語(yǔ)句塊中" ); ??
  12. ????????????????} ??
  13. ????????????????System.out.println( "我被執(zhí)行了-在try{}語(yǔ)句塊后" ); ??
  14. ????????????} ??
  15. ????????}; ??
  16. ???????? //?啟動(dòng)線程 ??
  17. ????????t.start(); ??
  18. ???????? //?在sleep()結(jié)束前中斷它 ??
  19. ????????t.interrupt(); ??
  20. ????} ??
  21. }??
    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é)果:

  1. 我被執(zhí)行了-在sleep()方法前
  2. 我被執(zhí)行了-在catch語(yǔ)句塊中
  3. 我被執(zhí)行了-在try{}語(yǔ)句塊后


wait()方法也是本地方法,屬于Object類(lèi),有三個(gè)定義:

Java代碼 復(fù)制代碼
  1. public ? final ? void ?wait()? throws ?InterruptedException?{ ??
  2. ???? //do?something ??
  3. } ??
  4. ??
  5. public ? final ? native ? void ?wait( long ?timeout)? throws ?InterruptedException; ??
  6. ??
  7. public ? final ? void ?wait( long ?timeout,? int ?nanos)? throws ?InterruptedException?{ ??
  8. ???? //do?something ??
  9. }??
    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è)同步方法的示例:

Java代碼 復(fù)制代碼
  1. public ? synchronized ? void ?aMethod()?{ ??
  2. ???? //?do?something ??
  3. } ??
  4. ??
  5. public ? static ? synchronized ? void ?anotherMethod()?{ ??
  6. ???? //?do?something ??
  7. }??
    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)方式:

Java代碼 復(fù)制代碼
  1. public ? void ?test()?{ ??
  2. ???? //?同步鎖 ??
  3. ????String?lock?=? "LOCK" ; ??
  4. ??
  5. ???? //?同步塊 ??
  6. ???? synchronized ?(lock)?{ ??
  7. ???????? //?do?something ??
  8. ????} ??
  9. ??
  10. ???? int ?i?=? 0 ; ??
  11. ???? //?... ??
  12. }??
    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í)例:

Java代碼 復(fù)制代碼
  1. private ?Lock?lock?=? new ?ReentrantLock(); ??
  2. ??
  3. public ? void ?testLock()?{ ??
  4. ???? //?鎖定對(duì)象 ??
  5. ????lock.lock(); ??
  6. ???? try ?{ ??
  7. ???????? //?do?something ??
  8. ????}? finally ?{ ??
  9. ???????? //?釋放對(duì)對(duì)象的鎖定 ??
  10. ????????lock.unlock(); ??
  11. ????} ??
  12. }??
    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ā)死鎖:

  1. 互斥(Mutual exclusion): 線程所使用的資源中至少有一個(gè)是不能共享的,它在同一時(shí)刻只能由一個(gè)線程使用。
  2. 持有與等待(Hold and wait): 至少有一個(gè)線程已經(jīng)持有了資源,并且正在等待獲取其他的線程所持有的資源。
  3. 非搶占式(No pre-emption): 如果一個(gè)線程已經(jīng)持有了某個(gè)資源,那么在這個(gè)線程釋放這個(gè)資源之前,別的線程不能把它搶奪過(guò)去使用。
  4. 循環(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(任務(wù)): 這是個(gè)代表任務(wù)的抽象類(lèi),其中定義了一個(gè)deal()方法,繼承Task抽象類(lèi)的子類(lèi)需要實(shí)現(xiàn)這個(gè)方法,并把這個(gè)任務(wù)需要完成的具體工作在deal()方法編碼實(shí)現(xiàn)。線程池中的線程之所以被創(chuàng)建,就是為了執(zhí)行各種各樣數(shù)量繁多的任務(wù)的,為了方便線程對(duì)任務(wù)的處理,我們需要用Task抽象類(lèi)來(lái)保證任務(wù)的具體工作統(tǒng)一放在deal()方法里來(lái)完成,這樣也使代碼更加規(guī)范。
    Task的定義如下:
    Java代碼 復(fù)制代碼
    1. public ? abstract ? class ?Task?{ ??
    2. ???? public ? enum ?State?{ ??
    3. ???????? /*?新建?*/ NEW,? /*?執(zhí)行中?*/ RUNNING,? /*?已完成?*/ FINISHED ??
    4. ????} ??
    5. ??
    6. ???? //?任務(wù)狀態(tài) ??
    7. ???? private ?State?state?=?State.NEW; ??
    8. ??
    9. ???? public ? void ?setState(State?state)?{ ??
    10. ???????? this .state?=?state; ??
    11. ????} ??
    12. ??
    13. ???? public ?State?getState()?{ ??
    14. ???????? return ?state; ??
    15. ????} ??
    16. ??
    17. ???? public ? abstract ? void ?deal(); ??
    18. }??
          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(任務(wù)隊(duì)列): 在同一時(shí)刻,可能有很多任務(wù)需要執(zhí)行,而程序在同一時(shí)刻只能執(zhí)行一定數(shù)量的任務(wù),當(dāng)需要執(zhí)行的任務(wù)數(shù)超過(guò)了程序所能承受的任務(wù)數(shù)時(shí)怎么辦呢?這就有了先執(zhí)行哪些任務(wù),后執(zhí)行哪些任務(wù)的規(guī)則。TaskQueue類(lèi)就定義了這些規(guī)則中的一種,它采用的是FIFO(先進(jìn)先出,英文名是First In First Out)的方式,也就是按照任務(wù)到達(dá)的先后順序執(zhí)行。
    TaskQueue類(lèi)的定義如下:
    Java代碼 復(fù)制代碼
    1. import ?java.util.Iterator; ??
    2. import ?java.util.LinkedList; ??
    3. import ?java.util.List; ??
    4. ??
    5. public ? class ?TaskQueue?{ ??
    6. ???? private ?List<Task>?queue?=? new ?LinkedList<Task>(); ??
    7. ??
    8. ???? //?添加一項(xiàng)任務(wù) ??
    9. ???? public ? synchronized ? void ?addTask(Task?task)?{ ??
    10. ???????? if ?(task?!=? null )?{ ??
    11. ????????????queue.add(task); ??
    12. ????????} ??
    13. ????} ??
    14. ??
    15. ???? //?完成任務(wù)后將它從任務(wù)隊(duì)列中刪除 ??
    16. ???? public ? synchronized ? void ?finishTask(Task?task)?{ ??
    17. ???????? if ?(task?!=? null )?{ ??
    18. ????????????task.setState(Task.State.FINISHED); ??
    19. ????????????queue.remove(task); ??
    20. ????????} ??
    21. ????} ??
    22. ??
    23. ???? //?取得一項(xiàng)待執(zhí)行任務(wù) ??
    24. ???? public ? synchronized ?Task?getTask()?{ ??
    25. ????????Iterator<Task>?it?=?queue.iterator(); ??
    26. ????????Task?task; ??
    27. ???????? while ?(it.hasNext())?{ ??
    28. ????????????task?=?it.next(); ??
    29. ???????????? //?尋找一個(gè)新建的任務(wù) ??
    30. ???????????? if ?(Task.State.NEW.equals(task.getState()))?{ ??
    31. ???????????????? //?把任務(wù)狀態(tài)置為運(yùn)行中 ??
    32. ????????????????task.setState(Task.State.RUNNING); ??
    33. ???????????????? return ?task; ??
    34. ????????????} ??
    35. ????????} ??
    36. ???????? return ? null ; ??
    37. ????} ??
    38. }??
          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ù)。
  • TaskThread(執(zhí)行任務(wù)的線程): 它繼承自Thread類(lèi),專(zhuān)門(mén)用于執(zhí)行任務(wù)隊(duì)列中的待執(zhí)行任務(wù)。
    Java代碼 復(fù)制代碼
    1. public ? class ?TaskThread? extends ?Thread?{ ??
    2. ???? //?該線程所屬的線程池 ??
    3. ???? private ?ThreadPoolService?service; ??
    4. ??
    5. ???? public ?TaskThread(ThreadPoolService?tps)?{ ??
    6. ????????service?=?tps; ??
    7. ????} ??
    8. ??
    9. ???? public ? void ?run()?{ ??
    10. ???????? //?在線程池運(yùn)行的狀態(tài)下執(zhí)行任務(wù)隊(duì)列中的任務(wù) ??
    11. ???????? while ?(service.isRunning())?{ ??
    12. ????????????TaskQueue?queue?=?service.getTaskQueue(); ??
    13. ????????????Task?task?=?queue.getTask(); ??
    14. ???????????? if ?(task?!=? null )?{ ??
    15. ????????????????task.deal(); ??
    16. ????????????} ??
    17. ????????????queue.finishTask(task); ??
    18. ????????} ??
    19. ????} ??
    20. }??
          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(線程池服務(wù)類(lèi)): 這是線程池最核心的一個(gè)類(lèi)。它在被創(chuàng)建了時(shí)候就創(chuàng)建了幾個(gè)線程對(duì)象,但是這些線程并沒(méi)有啟動(dòng)運(yùn)行,但調(diào)用了start()方法啟動(dòng)線程池服務(wù)時(shí),它們才真正運(yùn)行。stop()方法可以停止線程池服務(wù),同時(shí)停止池中所有線程的運(yùn)行。而runTask(Task task)方法是將一個(gè)新的待執(zhí)行任務(wù)交與線程池來(lái)運(yùn)行。
    ThreadPoolService類(lèi)的定義如下:
    Java代碼 復(fù)制代碼
    1. import ?java.util.ArrayList; ??
    2. import ?java.util.List; ??
    3. ??
    4. public ? class ?ThreadPoolService?{ ??
    5. ???? //?線程數(shù) ??
    6. ???? public ? static ? final ? int ?THREAD_COUNT?=? 5 ; ??
    7. ??
    8. ???? //?線程池狀態(tài) ??
    9. ???? private ?Status?status?=?Status.NEW; ??
    10. ??
    11. ???? private ?TaskQueue?queue?=? new ?TaskQueue(); ??
    12. ??
    13. ???? public ? enum ?Status?{ ??
    14. ???????? /*?新建?*/ NEW,? /*?提供服務(wù)中?*/ RUNNING,? /*?停止服務(wù)?*/ TERMINATED, ??
    15. ????} ??
    16. ??
    17. ???? private ?List<Thread>?threads?=? new ?ArrayList<Thread>(); ??
    18. ??
    19. ???? public ?ThreadPoolService()?{ ??
    20. ???????? for ?( int ?i?=? 0 ;?i?<?THREAD_COUNT;?i++)?{ ??
    21. ????????????Thread?t?=? new ?TaskThread( this ); ??
    22. ????????????threads.add(t); ??
    23. ????????} ??
    24. ????} ??
    25. ??
    26. ???? //?啟動(dòng)服務(wù) ??
    27. ???? public ? void ?start()?{ ??
    28. ???????? this .status?=?Status.RUNNING; ??
    29. ???????? for ?( int ?i?=? 0 ;?i?<?THREAD_COUNT;?i++)?{ ??
    30. ????????????threads.get(i).start(); ??
    31. ????????} ??
    32. ????} ??
    33. ??
    34. ???? //?停止服務(wù) ??
    35. ???? public ? void ?stop()?{ ??
    36. ???????? this .status?=?Status.TERMINATED; ??
    37. ????} ??
    38. ??
    39. ???? //?是否正在運(yùn)行 ??
    40. ???? public ? boolean ?isRunning()?{ ??
    41. ???????? return ?status?==?Status.RUNNING; ??
    42. ????} ??
    43. ??
    44. ???? //?執(zhí)行任務(wù) ??
    45. ???? public ? void ?runTask(Task?task)?{ ??
    46. ????????queue.addTask(task); ??
    47. ????} ??
    48. ??
    49. ???? protected ?TaskQueue?getTaskQueue()?{ ??
    50. ???????? return ?queue; ??
    51. ????} ??
    52. }??
          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)單的示例:
    Java代碼 復(fù)制代碼
    1. public ? class ?SimpleTaskTest? extends ?Task?{ ??
    2. ???? @Override ??
    3. ???? public ? void ?deal()?{ ??
    4. ???????? //?do?something ??
    5. ????} ??
    6. ??
    7. ???? public ? static ? void ?main(String[]?args)? throws ?InterruptedException?{ ??
    8. ????????ThreadPoolService?service?=? new ?ThreadPoolService(); ??
    9. ????????service.start(); ??
    10. ???????? //?執(zhí)行十次任務(wù) ??
    11. ???????? for ?(</spa
    分享到:
    評(píng)論
  • JAVA面試題解惑系列(十)——話說(shuō)多線程


    更多文章、技術(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ì)您有幫助就好】

    您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!?。?/p>

    發(fā)表我的評(píng)論
    最新評(píng)論 總共0條評(píng)論
    主站蜘蛛池模板: 国产精品99久久久久久www | 国产精品久久久久无码人妻 | gvg668| 色综合久久综合中文小说 | av官网在线 | 久久免费看少妇高潮A片JA | 亚洲精品久久久久久国产精华液 | 中文字幕 国产精品 | 国产在线精品一区二区三区 | 国产香蕉视频在线观看 | 免费观看黄的小视频 | 午夜在线观看cao | 5060网午夜 | 色屁屁www影院免费观看视频 | 最全精品自拍视频在线 | 五月婷婷久 | 久久国产欧美日韩精品 | 国产成人久久婷婷精品流白浆 | 人人草在线 | 视频三区 | 欧美人妖在线 | 日韩中文字幕在线播放 | 久久99精品久久久久久噜噜 | 亚洲免费中文字幕 | 欧美日韩国产在线 | 欧美久草视频 | 日本黄色网址免费 | 黑人性猛交xxxx乱大交一 | 日本视频免费高清一本18 | 亚洲成a人片在线观看中文 在线a人片免费观看国产 | 欧美成人一级 | 免费成人在线观看 | 久久综合九色综合欧美播 | 免费网址在线观看 | 日韩在线看片 | 天天色图片| 亚洲福利电影网 | 日本三级韩国三级香港三级a级 | 播放一级毛片 | 欧美国产精品一区 | 国产精品久久久久久久久久久久久 |