1. 程式人生 > >【JAVA秒會技術之玩轉多執行緒】多執行緒那些事兒(一)

【JAVA秒會技術之玩轉多執行緒】多執行緒那些事兒(一)

多執行緒那些事兒(一)

現在只要出去面試,關於“Java多執行緒”的問題,幾乎沒有一家單位不問的,可見其重要性。於是博主抽空研究了一下,確實很有意思!以下是我綜合整理了網上的各種資料,和個人的一些理解,寫的一篇總結博文,僅供學習、交流。

(一)多執行緒的概念

        多執行緒的概念,簡單理解:一個程序執行時產生了不止一個執行緒

        程序的概念,簡單理解:正在執行的程式的例項

兩者之間的關係:程序就是執行緒的容器

(二)多執行緒的好處

        使用多執行緒可以提高CPU的利用率。在多執行緒程式中,一個執行緒必須等待的時候,CPU可以執行其它的執行緒而不是等待,這樣就大大提高了程式的效率。

(三)執行緒狀態轉換

 

1新建狀態(New):新建立了一個執行緒物件。

2就緒狀態(Runnable):執行緒物件建立後,其他執行緒呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,變得可執行,等待獲取CPU的使用權。

3執行狀態(Running):就緒狀態的執行緒獲取了CPU,執行程式程式碼。

4阻塞狀態(Blocked):阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種:

        ①等待阻塞:執行的執行緒執行wait()方法,JVM會把該執行緒放入等待池中。

        ②同步阻塞

:執行的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入鎖池中。

        ③其他阻塞:執行的執行緒執行sleep()join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。

5死亡狀態(Dead):執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。

(五)建立執行緒的方式

       建立執行緒的方式主要有兩種:

  (1)一種是繼承Tread

package com.liyan.Test;
public class MyThread extends Thread {  //繼承Tread類
	@Override
	public void run(){                      //重寫run方法
		try{
			.......
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		MyThread myThread = new MyThread();  //產生新的執行緒物件
		myThread.start();                        //開啟執行緒
	}	
}


  (2)一種是實現Runnable介面

package com.liyan.gcTest;
public class MyThread implements Runnable { //實現Runnable介面
	@Override
	public void run(){   //重寫run方法
		try{
			.......
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		MyThread myThread = new MyThread();    //產生新的執行緒物件
		Thread thread = new Thread(myThread);  //將建立的執行緒作為引數傳入
		thread.start();                            //開啟執行緒
	}	
}


(六)終止執行緒的方式

   1. 使用退出標誌終止執行緒

       run方法執行完後,執行緒就會退出。但有時run方法是永遠不會結束的。如在服務端程式中使用執行緒進行監聽客戶端請求,或是其他的需要迴圈處理的任務。在這種情況下,一般是將這些任務放在一個迴圈中,如while迴圈。如果想讓迴圈永遠執行下去,可以使用whiletrue{……}來處理。但要想使while迴圈在某一特定條件下退出,最直接的方法就是設一個boolean型別的標誌,並通過設定這個標誌為truefalse來控制while迴圈是否退出。下面給出了一個利用退出標誌終止執行緒的例子。

package com.liyan.thread;
public class ThreadDemo extends Thread {
	public volatile boolean exit = false;
	public void run() {
		while (!exit);
	}

	public static void main(String[] args) throws Exception {
		ThreadDemo thread = new ThreadDemo();
		thread.start();
		sleep(5000); // 主執行緒延遲5秒
		thread.exit = true; // 終止執行緒thread
		thread.join();
		System.out.println("執行緒退出!");
	}
}

2. 使用stop方法終止執行緒   在上面程式碼中定義了一個退出標誌exit,當exittrue時,while迴圈退出,exit的預設值為false.在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻只能由一個執行緒來修改exit的值

    使用stop方法可以強行終止正在執行或掛起的執行緒。我們可以使用如下的程式碼來終止執行緒:

thread.stop();

3. 使用interrupt方法終止執行緒    雖然使用上面的程式碼可以終止執行緒,但使用stop方法是很危險的,就象突然關閉計算機電源,而不是按正常程式關機一樣,可能會產生不可預料的結果,因此,並不推薦使用stop方法來終止執行緒。

    使用interrupt方法來終端執行緒可分為兩種情況:

   (1)執行緒處於阻塞狀態,如使用了sleep方法。

   (2)使用while(!isInterrupted()){……}來判斷執行緒是否被中斷。

    在第一種情況下使用interrupt方法,sleep方法將丟擲一個InterruptedException例外,在第二種情況下執行緒將直接退出。下面的程式碼演示了在第一種情況下使用interrupt方法。

package com.liyan.thread;
public class ThreadDemo extends Thread {
	public void run() {
		try {
			sleep(50000); // 延遲50秒
		} catch (InterruptedException e) {
			System.out.println(e.getMessage());
		}
	}

	public static void main(String[] args) throws Exception {
		Thread thread = new ThreadDemo();
		thread.start();
		System.out.println("在50秒之內按任意鍵中斷執行緒!");
		System.in.read();
		thread.interrupt();
		thread.join();
		System.out.println("執行緒已經退出!");
	}
}

    輸出結果:

在50秒之內按任意鍵中斷執行緒!
liyan
sleep interrupted
執行緒已經退出!

    在呼叫interrupt方法後,sleep方法丟擲異常,然後輸出錯誤資訊:sleep interrupted.

    注意:在Thread類中有兩個方法可以判斷執行緒是否通過interrupt方法被終止。一個是靜態的方法interrupted(),一個是非靜態的方法isInterrupted(),這兩個方法的區別是interrupted用來判斷當前線是否被中斷,而isInterrupted可以用來判斷其他執行緒是否被中斷。因此,while(!isInterrupted())也可以換成while(!Thread.interrupted())。

   【參考:http://www.bitscn.com/pdb/java/200904/161228_3.html

(七)Daemon執行緒

    在Java中有兩類執行緒:User Thread(使用者執行緒)Daemon Thread(守護執行緒)

    用個比較通俗的比如,任何一個守護執行緒都是整個JVM中所有非守護執行緒的保姆:

    只要當前JVM例項中尚存在任何一個非守護執行緒沒有結束,守護執行緒就全部工作;只有當最後一個非守護執行緒結束時,守護執行緒隨著JVM一同結束工作。

        Daemon的作用是為其他執行緒的執行提供便利服務,它的優先順序比較低,用於為系統中的其它物件和執行緒提供服務。守護執行緒不依賴於終端,但是依賴於系統,與系統“同生共死”

    守護執行緒最典型的應用就是GC (垃圾回收器),當我們的程式中不再有任何執行的hread,程式就不會再產生垃圾,垃圾回收器也就無事可做,所以當垃圾回收執行緒是JVM上僅剩的執行緒時,垃圾回收執行緒會自動離開。它始終在低級別的狀態中執行,用於實時監控和管理系統中的可回收資源。

         UserDaemon兩者幾乎沒有區別,唯一的不同之處就在於虛擬機器的離開:如果User Thread已經全部退出執行了,只剩下Daemon Thread存在了,虛擬機器也就退出了。 因為沒有了被守護者,Daemon也就沒有工作可做了,也就沒有繼續執行程式的必要了。

  // 設定 daemonThread 為 守護執行緒,default false(非守護執行緒)
  daemonThread.setDaemon(true);
	
   // 驗證當前執行緒是否為守護執行緒,返回 true 則為守護執行緒
   daemonThread.isDaemon();

   (1thread.setDaemon(true)必須在thread.start()之前設定,否則會跑出一個IllegalThreadStateException異常。你不能把正在執行的常規執行緒設定為守護執行緒。      這裡有幾點需要注意

   (2Daemon執行緒中產生的新執行緒也是Daemon

   (3不要認為所有的應用都可以分配給Daemon來進行服務,比如讀寫操作或者計算邏輯因為你不可能知道在所有的User完成之前,Daemon是否已經完成了預期的服務任務。一旦User退出了,可能大量資料還沒有來得及讀入或寫出,計算任務也可能多次執行結果不一樣。這對程式是毀滅性的。造成這個結果理由已經說過了:一旦所有User Thread離開了,虛擬機器也就退出執行了。

   【參考:http://blog.csdn.net/shimiso/article/details/8964414