日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

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

系統(tǒng) 2386 0

好東西分享

線程或者說多線程,是我們處理多任務(wù)的強大工具。線程和進程是不同的,每個進程都是一個獨立運行的程序,擁有自己的變量,且不同進程間的變量不能共享;而線程是運行在進程內(nèi)部的,每個正在運行的進程至少有一個線程,而且不同的線程之間可以在進程范圍內(nèi)共享數(shù)據(jù)。也就是說進程有自己獨立的存儲空間,而線程是和它所屬的進程內(nèi)的其他線程共享一個存儲空間。線程的使用可以使我們能夠并行地處理一些事情。線程通過并行的處理給用戶帶來更好的使用體驗,比如你使用的郵件系統(tǒng)(outlook、Thunderbird、foxmail等),你當(dāng)然不希望它們在收取新郵件的時候,導(dǎo)致你連已經(jīng)收下來的郵件都無法閱讀,而只能等待收取郵件操作執(zhí)行完畢。這正是線程的意義所在。

實現(xiàn)線程的方式

實現(xiàn)線程的方式有兩種:

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


這是繼承Thread類實現(xiàn)線程的示例:

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

  


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

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

  


這兩種實現(xiàn)方式的區(qū)別并不大。繼承Thread類的方式實現(xiàn)起來較為簡單,但是繼承它的類就不能再繼承別的類了,因此也就不能繼承別的類的有用的方法了。而使用是想Runnable接口的方式就不存在這個問題了,而且這種實現(xiàn)方式將線程主體和線程對象本身分離開來,邏輯上也較為清晰,所以推薦大家更多地采用這種方式。

如何啟動線程

我們通過以上兩種方式實現(xiàn)了一個線程之后,線程的實例并沒有被創(chuàng)建,因此它們也并沒有被運行。我們要啟動一個線程,必須調(diào)用方法來啟動它,這個方法就是Thread類的start()方法,而不是run()方法(既不是我們繼承Thread類重寫的run()方法,也不是實現(xiàn)Runnable接口的run()方法)。run()方法中包含的是線程的主體,也就是這個線程被啟動后將要運行的代碼,它跟線程的啟動沒有任何關(guān)系。上面兩種實現(xiàn)線程的方式在啟動時會有所不同。

繼承Thread類的啟動方式:

Java代碼 復(fù)制代碼
  1. public ? class ?ThreadStartTest?{ ??
  2. ???? public ? static ? void ?main(String[]?args)?{ ??
  3. ???????? //?創(chuàng)建一個線程實例 ??
  4. ????????ThreadTest?tt?=? new ?ThreadTest(); ??
  5. ???????? //?啟動線程 ??
  6. ????????tt.start(); ??
  7. ????} ??
  8. }??
    public class ThreadStartTest {
	public static void main(String[] args) {
		// 創(chuàng)建一個線程實例
		ThreadTest tt = new ThreadTest();
		// 啟動線程
		tt.start();
	}
}

  


實現(xiàn)Runnable接口的啟動方式:

Java代碼 復(fù)制代碼
  1. public ? class ?RunnableStartTest?{ ??
  2. ???? public ? static ? void ?main(String[]?args)?{ ??
  3. ???????? //?創(chuàng)建一個線程實例 ??
  4. ????????Thread?t?=? new ?Thread( new ?RunnableTest()); ??
  5. ???????? //?啟動線程 ??
  6. ????????t.start(); ??
  7. ????} ??
  8. }??
    public class RunnableStartTest {
	public static void main(String[] args) {
		// 創(chuàng)建一個線程實例
		Thread t = new Thread(new RunnableTest());
		// 啟動線程
		t.start();
	}
}

  


實際上這兩種啟動線程的方式原理是一樣的。首先都是調(diào)用本地方法啟動一個線程,其次是在這個線程里執(zhí)行目標(biāo)對象的run()方法。那么這個目標(biāo)對象是什么呢?為了弄明白這個問題,我們來看看Thread類的run()方法的實現(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)我們采用實現(xiàn)Runnable接口的方式來實現(xiàn)線程的情況下,在調(diào)用new Thread(Runnable target)構(gòu)造器時,將實現(xiàn)Runnable接口的類的實例設(shè)置成了線程要執(zhí)行的主體所屬的目標(biāo)對象target,當(dāng)線程啟動時,這個實例的run()方法就被執(zhí)行了。當(dāng)我們采用繼承Thread的方式實現(xiàn)線程時,線程的這個run()方法被重寫了,所以當(dāng)線程啟動時,執(zhí)行的是這個對象自身的run()方法。總結(jié)起來就一句話,線程類有一個Runnable類型的target屬性,它是線程啟動后要執(zhí)行的run()方法所屬的主體,如果我們采用的是繼承Thread類的方式,那么這個target就是線程對象自身,如果我們采用的是實現(xiàn)Runnable接口的方式,那么這個target就是實現(xiàn)了Runnable接口的類的實例。

線程的狀態(tài)

在Java 1.4及以下的版本中,每個線程都具有新建、可運行、阻塞、死亡四種狀態(tài),但是在Java 5.0及以上版本中,線程的狀態(tài)被擴充為新建、可運行、阻塞、等待、定時等待、死亡六種。線程的狀態(tài)完全包含了一個線程從新建到運行,最后到結(jié)束的整個生命周期。線程狀態(tài)的具體信息如下:

  1. NEW(新建狀態(tài)、初始化狀態(tài)): 線程對象已經(jīng)被創(chuàng)建,但是還沒有被啟動時的狀態(tài)。這段時間就是在我們調(diào)用new命令之后,調(diào)用start()方法之前。
  2. RUNNABLE(可運行狀態(tài)、就緒狀態(tài)): 在我們調(diào)用了線程的start()方法之后線程所處的狀態(tài)。處于RUNNABLE狀態(tài)的線程在JAVA虛擬機(JVM)上是運行著的,但是它可能還正在等待操作系統(tǒng)分配給它相應(yīng)的運行資源以得以運行。
  3. BLOCKED(阻塞狀態(tài)、被中斷運行): 線程正在等待其它的線程釋放同步鎖,以進入一個同步塊或者同步方法繼續(xù)運行;或者它已經(jīng)進入了某個同步塊或同步方法,在運行的過程中它調(diào)用了某個對象繼承自java.lang.Object的wait()方法,正在等待重新返回這個同步塊或同步方法。
  4. WAITING(等待狀態(tài)): 當(dāng)前線程調(diào)用了java.lang.Object.wait()、java.lang.Thread.join()或者java.util.concurrent.locks.LockSupport.park()三個中的任意一個方法,正在等待另外一個線程執(zhí)行某個操作。比如一個線程調(diào)用了某個對象的wait()方法,正在等待其它線程調(diào)用這個對象的notify()或者notifyAll()(這兩個方法同樣是繼承自O(shè)bject類)方法來喚醒它;或者一個線程調(diào)用了另一個線程的join()(這個方法屬于Thread類)方法,正在等待這個方法運行結(jié)束。
  5. TIMED_WAITING(定時等待狀態(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)四個方法中的任意一個,進入等待狀態(tài),但是與WAITING狀態(tài)不同的是,它有一個最大等待時間,即使等待的條件仍然沒有滿足,只要到了這個時間它就會自動醒來。
  6. TERMINATED(死亡狀態(tài)、終止?fàn)顟B(tài)): 線程完成執(zhí)行后的狀態(tài)。線程執(zhí)行完run()方法中的全部代碼,從該方法中退出,進入TERMINATED狀態(tài)。還有一種情況是run()在運行過程中拋出了一個異常,而這個異常沒有被程序捕獲,導(dǎo)致這個線程異常終止進入TERMINATED狀態(tài)。


在Java5.0及以上版本中,線程的全部六種狀態(tài)都以枚舉類型的形式定義在java.lang.Thread類中了,代碼如下:

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)前運行的線程停止運行的效果,這是它們的共同點。下面我們來詳細說說它們的不同之處。

sleep()方法是本地方法,屬于Thread類,它有兩種定義:

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ù)(十億分之一秒)。這兩個方法都可以讓調(diào)用它的線程沉睡(停止運行)指定的時間,到了這個時間,線程就會自動醒來,變?yōu)榭蛇\行狀態(tài)(RUNNABLE),但這并不表示它馬上就會被運行,因為線程調(diào)度機制恢復(fù)線程的運行也需要時間。調(diào)用sleep()方法并不會讓線程釋放它所持有的同步鎖;而且在這期間它也不會阻礙其它線程的運行。上面的連個方法都聲明拋出一個InterruptedException類型的異常,這是因為線程在sleep()期間,有可能被持有它的引用的其它線程調(diào)用它的interrupt()方法而中斷。中斷一個線程會導(dǎo)致一個InterruptedException異常的產(chǎn)生,如果你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態(tài),如果你的程序捕獲了這個異常,那么程序就會繼續(xù)執(zhí)行catch語句塊(可能還有finally語句塊)以及以后的代碼。

為了更好地理解interrupt()效果,我們來看一下下面這個例子:

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. ???????????????????? //?停止運行10分鐘 ??
  8. ????????????????????Thread.sleep( 1000 ?*? 60 ?*? 10 ); ??
  9. ????????????????????System.out.println( "我被執(zhí)行了-在sleep()方法后" ); ??
  10. ????????????????}? catch ?(InterruptedException?e)?{ ??
  11. ????????????????????System.out.println( "我被執(zhí)行了-在catch語句塊中" ); ??
  12. ????????????????} ??
  13. ????????????????System.out.println( "我被執(zhí)行了-在try{}語句塊后" ); ??
  14. ????????????} ??
  15. ????????}; ??
  16. ???????? //?啟動線程 ??
  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()方法前");
					// 停止運行10分鐘
					Thread.sleep(1000 * 60 * 10);
					System.out.println("我被執(zhí)行了-在sleep()方法后");
				} catch (InterruptedException e) {
					System.out.println("我被執(zhí)行了-在catch語句塊中");
				}
				System.out.println("我被執(zhí)行了-在try{}語句塊后");
			}
		};
		// 啟動線程
		t.start();
		// 在sleep()結(jié)束前中斷它
		t.interrupt();
	}
}

  


運行結(jié)果:

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


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

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)方法實現(xiàn)的。同樣地,timeout代表毫秒數(shù),nanos代表納秒數(shù)。當(dāng)調(diào)用了某個對象的wait()方法時,當(dāng)前運行的線程就會轉(zhuǎn)入等待狀態(tài)(WAITING),等待別的線程再次調(diào)用這個對象的notify()或者notifyAll()方法(這兩個方法也是本地方法)喚醒它,或者到了指定的最大等待時間,線程自動醒來。如果線程擁有某個或某些對象的同步鎖,那么在調(diào)用了wait()后,這個線程就會釋放它持有的所有同步資源,而不限于這個被調(diào)用了wait()方法的對象。wait()方法同樣會被Thread類的interrupt()方法中斷,并產(chǎn)生一個InterruptedException異常,效果同sleep()方法被中斷一樣。

實現(xiàn)同步的方式

同步是多線程中的重要概念。同步的使用可以保證在多線程運行的環(huán)境中,程序不會產(chǎn)生設(shè)計之外的錯誤結(jié)果。同步的實現(xiàn)方式有兩種,同步方法和同步塊,這兩種方式都要用到synchronized關(guān)鍵字。

給一個方法增加synchronized修飾符之后就可以使它成為同步方法,這個方法可以是靜態(tài)方法和非靜態(tài)方法,但是不能是抽象類的抽象方法,也不能是接口中的接口方法。下面代碼是一個同步方法的示例:

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í)行同步方法時是具有排它性的。當(dāng)任意一個線程進入到一個對象的任意一個同步方法時,這個對象的所有同步方法都被鎖定了,在此期間,其他任何線程都不能訪問這個對象的任意一個同步方法,直到這個線程執(zhí)行完它所調(diào)用的同步方法并從中退出,從而導(dǎo)致它釋放了該對象的同步鎖之后。在一個對象被某個線程鎖定之后,其他線程是可以訪問這個對象的所有非同步方法的。

同步塊的形式雖然與同步方法不同,但是原理和效果是一致的。同步塊是通過鎖定一個指定的對象,來對同步塊中包含的代碼進行同步;而同步方法是對這個方法塊里的代碼進行同步,而這種情況下鎖定的對象就是同步方法所屬的主體對象自身。如果這個方法是靜態(tài)同步方法呢?那么線程鎖定的就不是這個類的對象了,也不是這個類自身,而是這個類對應(yīng)的java.lang.Class類型的對象。同步方法和同步塊之間的相互制約只限于同一個對象之間,所以靜態(tài)同步方法只受它所屬類的其它靜態(tài)同步方法的制約,而跟這個類的實例(對象)沒有關(guān)系。

下面這段代碼演示了同步塊的實現(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;
	// ...
}

  


對于作為同步鎖的對象并沒有什么特別要求,任意一個對象都可以。如果一個對象既有同步方法,又有同步塊,那么當(dāng)其中任意一個同步方法或者同步塊被某個線程執(zhí)行時,這個對象就被鎖定了,其他線程無法在此時訪問這個對象的同步方法,也不能執(zhí)行同步塊。

synchronized和Lock

Lock是一個接口,它位于Java 5.0新增的java.utils.concurrent包的子包locks中。concurrent包及其子包中的類都是用來處理多線程編程的。實現(xiàn)Lock接口的類具有與synchronized關(guān)鍵字同樣的功能,但是它更加強大一些。java.utils.concurrent.locks.ReentrantLock是較常用的實現(xiàn)了Lock接口的類。下面是ReentrantLock類的一個應(yīng)用實例:

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

public void testLock() {
	// 鎖定對象
	lock.lock();
	try {
		// do something
	} finally {
		// 釋放對對象的鎖定
		lock.unlock();
	}
}

  


lock()方法用于鎖定對象,unlock()方法用于釋放對對象的鎖定,他們都是在Lock接口中定義的方法。位于這兩個方法之間的代碼在被執(zhí)行時,效果等同于被放在synchronized同步塊中。一般用法是將需要在lock()和unlock()方法之間執(zhí)行的代碼放在try{}塊中,并且在finally{}塊中調(diào)用unlock()方法,這樣就可以保證即使在執(zhí)行代碼拋出異常的情況下,對象的鎖也總是會被釋放,否則的話就會為死鎖的產(chǎn)生增加可能。

使用synchronized關(guān)鍵字實現(xiàn)的同步,會把一個對象的所有同步方法和同步塊看做一個整體,只要有一個被某個線程調(diào)用了,其他的就無法被別的線程執(zhí)行,即使這些方法或同步塊與被調(diào)用的代碼之間沒有任何邏輯關(guān)系,這顯然降低了程序的運行效率。而使用Lock就能夠很好地解決這個問題。我們可以把一個對象中按照邏輯關(guān)系把需要同步的方法或代碼進行分組,為每個組創(chuàng)建一個Lock類型的對象,對實現(xiàn)同步。那么,當(dāng)一個同步塊被執(zhí)行時,這個線程只會鎖定與當(dāng)前運行代碼相關(guān)的其他代碼最小集合,而并不影響其他線程對其余同步代碼的調(diào)用執(zhí)行。

關(guān)于死鎖

死鎖就是一個進程中的每個線程都在等待這個進程中的其他線程釋放所占用的資源,從而導(dǎo)致所有線程都無法繼續(xù)執(zhí)行的情況。死鎖是多線程編程中一個隱藏的陷阱,它經(jīng)常發(fā)生在多個線程共用資源的時候。在實際開發(fā)中,死鎖一般隱藏的較深,不容易被發(fā)現(xiàn),一旦死鎖現(xiàn)象發(fā)生,就必然會導(dǎo)致程序的癱瘓。因此必須避免它的發(fā)生。

程序中必須同時滿足以下四個條件才會引發(fā)死鎖:

  1. 互斥(Mutual exclusion): 線程所使用的資源中至少有一個是不能共享的,它在同一時刻只能由一個線程使用。
  2. 持有與等待(Hold and wait): 至少有一個線程已經(jīng)持有了資源,并且正在等待獲取其他的線程所持有的資源。
  3. 非搶占式(No pre-emption): 如果一個線程已經(jīng)持有了某個資源,那么在這個線程釋放這個資源之前,別的線程不能把它搶奪過去使用。
  4. 循環(huán)等待(Circular wait): 假設(shè)有N個線程在運行,第一個線程持有了一個資源,并且正在等待獲取第二個線程持有的資源,而第二個線程正在等待獲取第三個線程持有的資源,依此類推……第N個線程正在等待獲取第一個線程持有的資源,由此形成一個循環(huán)等待。


線程池

線程池就像數(shù)據(jù)庫連接池一樣,是一個對象池。所有的對象池都有一個共同的目的,那就是為了提高對象的使用率,從而達到提高程序效率的目的。比如對于Servlet,它被設(shè)計為多線程的(如果它是單線程的,你就可以想象,當(dāng)1000個人同時請求一個網(wǎng)頁時,在第一個人獲得請求結(jié)果之前,其它999個人都在郁悶地等待),如果為每個用戶的每一次請求都創(chuàng)建一個新的線程對象來運行的話,系統(tǒng)就會在創(chuàng)建線程和銷毀線程上耗費很大的開銷,大大降低系統(tǒng)的效率。因此,Servlet多線程機制背后有一個線程池在支持,線程池在初始化初期就創(chuàng)建了一定數(shù)量的線程對象,通過提高對這些對象的利用率,避免高頻率地創(chuàng)建對象,從而達到提高程序的效率的目的。

下面實現(xiàn)一個最簡單的線程池,從中理解它的實現(xiàn)原理。為此我們定義了四個類,它們的用途及具體實現(xiàn)如下:
[list=1]

  • Task(任務(wù)): 這是個代表任務(wù)的抽象類,其中定義了一個deal()方法,繼承Task抽象類的子類需要實現(xiàn)這個方法,并把這個任務(wù)需要完成的具體工作在deal()方法編碼實現(xiàn)。線程池中的線程之所以被創(chuàng)建,就是為了執(zhí)行各種各樣數(shù)量繁多的任務(wù)的,為了方便線程對任務(wù)的處理,我們需要用Task抽象類來保證任務(wù)的具體工作統(tǒng)一放在deal()方法里來完成,這樣也使代碼更加規(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ù)隊列): 在同一時刻,可能有很多任務(wù)需要執(zhí)行,而程序在同一時刻只能執(zhí)行一定數(shù)量的任務(wù),當(dāng)需要執(zhí)行的任務(wù)數(shù)超過了程序所能承受的任務(wù)數(shù)時怎么辦呢?這就有了先執(zhí)行哪些任務(wù),后執(zhí)行哪些任務(wù)的規(guī)則。TaskQueue類就定義了這些規(guī)則中的一種,它采用的是FIFO(先進先出,英文名是First In First Out)的方式,也就是按照任務(wù)到達的先后順序執(zhí)行。
    TaskQueue類的定義如下:
    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. ???? //?添加一項任務(wù) ??
    9. ???? public ? synchronized ? void ?addTask(Task?task)?{ ??
    10. ???????? if ?(task?!=? null )?{ ??
    11. ????????????queue.add(task); ??
    12. ????????} ??
    13. ????} ??
    14. ??
    15. ???? //?完成任務(wù)后將它從任務(wù)隊列中刪除 ??
    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. ???? //?取得一項待執(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. ???????????? //?尋找一個新建的任務(wù) ??
    30. ???????????? if ?(Task.State.NEW.equals(task.getState()))?{ ??
    31. ???????????????? //?把任務(wù)狀態(tài)置為運行中 ??
    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>();
    
    	// 添加一項任務(wù)
    	public synchronized void addTask(Task task) {
    		if (task != null) {
    			queue.add(task);
    		}
    	}
    
    	// 完成任務(wù)后將它從任務(wù)隊列中刪除
    	public synchronized void finishTask(Task task) {
    		if (task != null) {
    			task.setState(Task.State.FINISHED);
    			queue.remove(task);
    		}
    	}
    
    	// 取得一項待執(zhí)行任務(wù)
    	public synchronized Task getTask() {
    		Iterator<Task> it = queue.iterator();
    		Task task;
    		while (it.hasNext()) {
    			task = it.next();
    			// 尋找一個新建的任務(wù)
    			if (Task.State.NEW.equals(task.getState())) {
    				// 把任務(wù)狀態(tài)置為運行中
    				task.setState(Task.State.RUNNING);
    				return task;
    			}
    		}
    		return null;
    	}
    }
    
        

    addTask(Task task)方法用于當(dāng)一個新的任務(wù)到達時,將它添加到任務(wù)隊列中。這里使用了LinkedList類來保存任務(wù)到達的先后順序。finishTask(Task task)方法用于任務(wù)被執(zhí)行完畢時,將它從任務(wù)隊列中清除出去。getTask()方法用于取得當(dāng)前要執(zhí)行的任務(wù)。
  • TaskThread(執(zhí)行任務(wù)的線程): 它繼承自Thread類,專門用于執(zhí)行任務(wù)隊列中的待執(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. ???????? //?在線程池運行的狀態(tài)下執(zhí)行任務(wù)隊列中的任務(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() {
    		// 在線程池運行的狀態(tài)下執(zhí)行任務(wù)隊列中的任務(wù)
    		while (service.isRunning()) {
    			TaskQueue queue = service.getTaskQueue();
    			Task task = queue.getTask();
    			if (task != null) {
    				task.deal();
    			}
    			queue.finishTask(task);
    		}
    	}
    }
    
        
  • ThreadPoolService(線程池服務(wù)類): 這是線程池最核心的一個類。它在被創(chuàng)建了時候就創(chuàng)建了幾個線程對象,但是這些線程并沒有啟動運行,但調(diào)用了start()方法啟動線程池服務(wù)時,它們才真正運行。stop()方法可以停止線程池服務(wù),同時停止池中所有線程的運行。而runTask(Task task)方法是將一個新的待執(zhí)行任務(wù)交與線程池來運行。
    ThreadPoolService類的定義如下:
    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. ???? //?啟動服務(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. ???? //?是否正在運行 ??
    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);
    		}
    	}
    
    	// 啟動服務(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;
    	}
    
    	// 是否正在運行
    	public boolean isRunning() {
    		return status == Status.RUNNING;
    	}
    
    	// 執(zhí)行任務(wù)
    	public void runTask(Task task) {
    		queue.addTask(task);
    	}
    
    	protected TaskQueue getTaskQueue() {
    		return queue;
    	}
    }
    
        

    [/list]
    完成了上面四個類,我們就實現(xiàn)了一個簡單的線程池。現(xià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
    分享到:
    評論
  • JAVA面試題解惑系列(十)——話說多線程


    更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

    微信掃碼或搜索:z360901061

    微信掃一掃加我為好友

    QQ號聯(lián)系: 360901061

    您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

    【本文對您有幫助就好】

    您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

    發(fā)表我的評論
    最新評論 總共0條評論
    主站蜘蛛池模板: 五峰| 赣州市| 奎屯市| 乌兰浩特市| 东宁县| 鲁甸县| 偃师市| 津市市| 扶余县| 太原市| 台江县| 历史| 海晏县| 巢湖市| 博爱县| 将乐县| 伊川县| 大冶市| 方山县| 乃东县| 呼伦贝尔市| 黄梅县| 永泰县| 定陶县| 佛冈县| 尤溪县| 许昌县| 乐陵市| 库伦旗| 永川市| 西青区| 藁城市| 都昌县| 广饶县| 明溪县| 秦安县| 桃源县| 遵化市| 凤山县| 岫岩| 洛扎县|