亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

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

系統 2047 0

好東西分享

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

實現線程的方式

實現線程的方式有兩種:

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


這是繼承Thread類實現線程的示例:

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

  


這是實現Runnable接口實現多線程的示例:

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

  


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

如何啟動線程

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

繼承Thread類的啟動方式:

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

  


實現Runnable接口的啟動方式:

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

  


實際上這兩種啟動線程的方式原理是一樣的。首先都是調用本地方法啟動一個線程,其次是在這個線程里執行目標對象的run()方法。那么這個目標對象是什么呢?為了弄明白這個問題,我們來看看Thread類的run()方法的實現:

Java代碼 復制代碼
  1. public ? void ?run()?{ ??
  2. ???? if ?(target?!=? null )?{ ??
  3. ????????target.run(); ??
  4. ????} ??
  5. }??
    public void run() {
	if (target != null) {
		target.run();
	}
}

  


當我們采用實現Runnable接口的方式來實現線程的情況下,在調用new Thread(Runnable target)構造器時,將實現Runnable接口的類的實例設置成了線程要執行的主體所屬的目標對象target,當線程啟動時,這個實例的run()方法就被執行了。當我們采用繼承Thread的方式實現線程時,線程的這個run()方法被重寫了,所以當線程啟動時,執行的是這個對象自身的run()方法??偨Y起來就一句話,線程類有一個Runnable類型的target屬性,它是線程啟動后要執行的run()方法所屬的主體,如果我們采用的是繼承Thread類的方式,那么這個target就是線程對象自身,如果我們采用的是實現Runnable接口的方式,那么這個target就是實現了Runnable接口的類的實例。

線程的狀態

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

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


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

Java代碼 復制代碼
  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()的區別

sleep()方法和wait()方法都成產生讓當前運行的線程停止運行的效果,這是它們的共同點。下面我們來詳細說說它們的不同之處。

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

Java代碼 復制代碼
  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
}

  


其中的參數millis代表毫秒數(千分之一秒),nanos代表納秒數(十億分之一秒)。這兩個方法都可以讓調用它的線程沉睡(停止運行)指定的時間,到了這個時間,線程就會自動醒來,變為可運行狀態(RUNNABLE),但這并不表示它馬上就會被運行,因為線程調度機制恢復線程的運行也需要時間。調用sleep()方法并不會讓線程釋放它所持有的同步鎖;而且在這期間它也不會阻礙其它線程的運行。上面的連個方法都聲明拋出一個InterruptedException類型的異常,這是因為線程在sleep()期間,有可能被持有它的引用的其它線程調用它的interrupt()方法而中斷。中斷一個線程會導致一個InterruptedException異常的產生,如果你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態,如果你的程序捕獲了這個異常,那么程序就會繼續執行catch語句塊(可能還有finally語句塊)以及以后的代碼。

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

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

  


運行結果:

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


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

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

實現同步的方式

同步是多線程中的重要概念。同步的使用可以保證在多線程運行的環境中,程序不會產生設計之外的錯誤結果。同步的實現方式有兩種,同步方法和同步塊,這兩種方式都要用到synchronized關鍵字。

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

Java代碼 復制代碼
  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
}

  


線程在執行同步方法時是具有排它性的。當任意一個線程進入到一個對象的任意一個同步方法時,這個對象的所有同步方法都被鎖定了,在此期間,其他任何線程都不能訪問這個對象的任意一個同步方法,直到這個線程執行完它所調用的同步方法并從中退出,從而導致它釋放了該對象的同步鎖之后。在一個對象被某個線程鎖定之后,其他線程是可以訪問這個對象的所有非同步方法的。

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

下面這段代碼演示了同步塊的實現方式:

Java代碼 復制代碼
  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;
	// ...
}

  


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

synchronized和Lock

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

Java代碼 復制代碼
  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接口中定義的方法。位于這兩個方法之間的代碼在被執行時,效果等同于被放在synchronized同步塊中。一般用法是將需要在lock()和unlock()方法之間執行的代碼放在try{}塊中,并且在finally{}塊中調用unlock()方法,這樣就可以保證即使在執行代碼拋出異常的情況下,對象的鎖也總是會被釋放,否則的話就會為死鎖的產生增加可能。

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

關于死鎖

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

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

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


線程池

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

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

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

    addTask(Task task)方法用于當一個新的任務到達時,將它添加到任務隊列中。這里使用了LinkedList類來保存任務到達的先后順序。finishTask(Task task)方法用于任務被執行完畢時,將它從任務隊列中清除出去。getTask()方法用于取得當前要執行的任務。
  • TaskThread(執行任務的線程): 它繼承自Thread類,專門用于執行任務隊列中的待執行任務。
    Java代碼 復制代碼
    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. ???????? //?在線程池運行的狀態下執行任務隊列中的任務 ??
    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() {
    		// 在線程池運行的狀態下執行任務隊列中的任務
    		while (service.isRunning()) {
    			TaskQueue queue = service.getTaskQueue();
    			Task task = queue.getTask();
    			if (task != null) {
    				task.deal();
    			}
    			queue.finishTask(task);
    		}
    	}
    }
    
        
  • ThreadPoolService(線程池服務類): 這是線程池最核心的一個類。它在被創建了時候就創建了幾個線程對象,但是這些線程并沒有啟動運行,但調用了start()方法啟動線程池服務時,它們才真正運行。stop()方法可以停止線程池服務,同時停止池中所有線程的運行。而runTask(Task task)方法是將一個新的待執行任務交與線程池來運行。
    ThreadPoolService類的定義如下:
    Java代碼 復制代碼
    1. import ?java.util.ArrayList; ??
    2. import ?java.util.List; ??
    3. ??
    4. public ? class ?ThreadPoolService?{ ??
    5. ???? //?線程數 ??
    6. ???? public ? static ? final ? int ?THREAD_COUNT?=? 5 ; ??
    7. ??
    8. ???? //?線程池狀態 ??
    9. ???? private ?Status?status?=?Status.NEW; ??
    10. ??
    11. ???? private ?TaskQueue?queue?=? new ?TaskQueue(); ??
    12. ??
    13. ???? public ? enum ?Status?{ ??
    14. ???????? /*?新建?*/ NEW,? /*?提供服務中?*/ RUNNING,? /*?停止服務?*/ 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. ???? //?啟動服務 ??
    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. ???? //?停止服務 ??
    35. ???? public ? void ?stop()?{ ??
    36. ???????? this .status?=?Status.TERMINATED; ??
    37. ????} ??
    38. ??
    39. ???? //?是否正在運行 ??
    40. ???? public ? boolean ?isRunning()?{ ??
    41. ???????? return ?status?==?Status.RUNNING; ??
    42. ????} ??
    43. ??
    44. ???? //?執行任務 ??
    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 {
    	// 線程數
    	public static final int THREAD_COUNT = 5;
    
    	// 線程池狀態
    	private Status status = Status.NEW;
    
    	private TaskQueue queue = new TaskQueue();
    
    	public enum Status {
    		/* 新建 */NEW, /* 提供服務中 */RUNNING, /* 停止服務 */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);
    		}
    	}
    
    	// 啟動服務
    	public void start() {
    		this.status = Status.RUNNING;
    		for (int i = 0; i < THREAD_COUNT; i++) {
    			threads.get(i).start();
    		}
    	}
    
    	// 停止服務
    	public void stop() {
    		this.status = Status.TERMINATED;
    	}
    
    	// 是否正在運行
    	public boolean isRunning() {
    		return status == Status.RUNNING;
    	}
    
    	// 執行任務
    	public void runTask(Task task) {
    		queue.addTask(task);
    	}
    
    	protected TaskQueue getTaskQueue() {
    		return queue;
    	}
    }
    
        

    [/list]
    完成了上面四個類,我們就實現了一個簡單的線程池?,F在我們就可以使用它了,下面的代碼做了一個簡單的示例:
    Java代碼 復制代碼
    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. ???????? //?執行十次任務 ??
    11. ???????? for ?(</spa
    分享到:
    評論
  • JAVA面試題解惑系列(十)——話說多線程


    更多文章、技術交流、商務合作、聯系博主

    微信掃碼或搜索:z360901061

    微信掃一掃加我為好友

    QQ號聯系: 360901061

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

    【本文對您有幫助就好】

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

    發表我的評論
    最新評論 總共0條評論
    主站蜘蛛池模板: 精品国产你懂的在线观看 | 五月天婷婷在线观看高清 | 天天舔天天舔 | 久久婷婷久久一区二区三区 | 999精品在线视频 | 超激情碰碰碰啪在线视频 | 欧美午夜网站 | 欧美日日日 | 亚洲国产欧洲精品路线久久 | 老司机久久精品 | 亚洲国产午夜精品理论片的软件 | 波多野结衣中文字幕一区 | 青青久久久 | 国产免费福利视频 | 欧美在线成人免费国产 | 亚洲精品一区二区三区四区手机版 | 久久久精品一区 | 国产一区二区日韩欧美在线 | 四虎成人在线 | 吃奶japanesevideo 处videossex第一次中 | 免费观看成人久久网免费观看 | 青青爽| 在线播放免费一级毛片欧美 | 91久久精品日日躁夜夜躁欧美 | 五月婷婷婷婷 | 精品久久久中文字幕二区 | 欧美一级二级aaa免费视频 | 欧美高清理论片在线观看 | 久草香蕉视频在线观看 | 狠狠大日本亚洲香蕉亚洲 | 99re6这里只有精品视频 | 国产人成精品综合欧美成人 | 不卡网| 久久这里只有精品免费播放 | 亚洲国产福利精品一区二区 | 国产在线拍国产拍拍偷 | 日韩中文字幕在线观看视频 | 2020久久精品永久免费 | 男人的天堂视频 | 亚洲精品在线网 | 国产中的精品一区的 |