Java中多線程總結

1.     創建線程有兩種方式:

1)擴展java.lang.Thread

    public class ThreadTest extends Thread{ }

2)實現Runnable接口

    public class ThreadTest implements Runnable{ }

2.     Thread類代表線程類,有兩個最主要的方法:

1run() 包含線程運行時所執行的代碼。

   用戶的線程類只需要繼承Thread類,覆蓋Thread類的run()方法即可。在Thread類中的run()方法沒有招聘任何異常,所以Thread子類的run()方法也不能聲明招聘任何異常。

2start() 用於啓動線程。

3.線程的運行過程:

   1)主線程與用戶自定義的線程併發運行

   2)多個線程共享一個對象的實例變量

   3)不要隨便覆蓋Thread類的start()方法,假如一定要覆蓋,就在重新定義的start()方法首語句中調用super.start()方法。

   4)一個線程只能被啓動一次

4.線程的狀態轉換:

   1)新建狀態

      new語句創建的線程對象處於新建狀態,此時它和其他Java對象一樣,僅僅在堆區中被分配了內存。

   2)就緒狀態

      當一個線程對象創建後,其他線程調用它的start()方法,該線程就進入就緒狀態,JVM會爲它創建方法調用棧和程序計數器。處於這個狀態的線程位於可運行池中,等待獲得CPU的使用權。

   3)運行狀態

      處於這個狀態的線程佔用CPU,執行程序代碼。在併發運行環境中,如果計算機只有一個CPU,那麼任何時刻只會有一個線程處於這個狀態。如果計算機有多個CPU,那麼同一時刻可以讓幾個線程佔用不同的CPU,使它們都處於運行狀態。只有牌就緒狀態的線程纔有機會轉到運行狀態。

   4)阻塞狀態

      阻塞狀態是指線程因爲某些原因放棄CPU,暫時停止運行。當線程處於阻塞狀態時,JVM不會給線程分配CPU,直到線程重新進入就緒狀態,它纔有機會轉到運行狀態。

      阻塞狀態可分爲以下3

      1))位於對象等等池中的阻塞狀態:當線程牌運行狀態時,如果執行了某個對象的wait()方法,JVM就會把線程放到這個對象的等待池中。

      2))位於對象鎖池中的阻塞狀態:當線程牌運行狀態,試圖獲得某個對象的同步鎖時,如果該對象的同步鎖已經被其他線程佔用,JVM就會把這個線程放到這個對旬的鎖池中。

      3))其他阻塞狀態:當前線程執行了sleep()方法,或者調用了其他線程的join()方法,或者發出了I/O請求時,就會進入這個狀態。

         當一個線程執行System.out.println()或者System.in.read()方法時,就會發出一個I/O請求,該線程放棄CPU,進入阻塞狀態,直到I/O處理完畢,該線程纔會恢復運行。

   5)死亡狀態

      當線程退出run()方法時,就進入死亡狀態,該線程結束生命週期。線程有可能是正常執行完run()方法而退出,也有可能是遇到異常而退出。不管線程正常結束還是異常結束,都不會對其他其他線程造成影響。

      Thread類的isAlive()方法判斷一個線程是否活着,當線程處於死亡狀態或者新建狀態時,該方法返回false,在其餘狀態下,該方法返回true.

5.線程的調度:

  有兩種調度方式:分時調度模型和搶佔式調度模型。

  分時調度模型是指讓所有線程輪流獲得CPU的使用權,並且平均分配每個線程佔用CPU的時間片。

  Java虛擬機採用搶佔式高度模型,是指優先讓可運行池中優先級高的線程佔用CPU,如果可運行池中線程的優先級相同,那麼就隨機選擇一個線程,使其佔用CPU。處於運行狀態的線程會一直運行,直至它不得不放棄CPU

  線程的調度不是跨平臺的,它不僅取決於JVM,還依賴於操作系統。

6Thread類的setPriority(int)getPriority()方法分別用來設置優先級和讀取優先級。

   1MAX_PRIORITY:取值爲10,表示最高優先級

   2MIN_PRIORITY:取值爲1,表示最低優先級

   3NORM_PRIORITY:取值爲5,表示默認的優先級

  如果希望程序能移植到各個操作系統中,應該確保在設置線程的優先級時,只使用MAX_PRIORITYMIN_PRIORITYNORM_PRIORITY這三個優先級。這樣才能保證在不同的操作系統中,對同樣優先級的線程採用同樣的調度方式。

7sleep()方法與yield()方法的區別:

   1sleep()方法會給其他線程運行的機會,而不考慮其他線程的優先級,因此會給較低優先級線程一個運行的機會;yield()方法只會給相同優先級或者更高優先級的線程一個運行的機會。

   2)當線程執行了sleep()方法後,將轉到阻塞狀態;當線程執行了yield()方法後,將轉到就緒狀態。

   3sleep()方法聲明拋出InterruptedException異常,而yield()方法沒有聲明招聘任何異常。

   4sleep()方法比yield()方法具有更好的可移植性。

8.等待其他線程結束:join()方法

   當前運行的線程可以調用另一個線程的join()方法,當前運行的線程將轉到阻塞狀態,直至另一個線程運行結束,它纔會恢復運行(確切的意思是指線程從阻塞狀態轉到就緒狀態)。

9.定時器Timer

   Timer timer=new Timer(true);

   TimerTask task=new TimerTask(){ }

   timer.schedule(task,10,500);//定時器將在10毫秒後開始執行task任務,以後每隔//500毫秒重複執行一次task任務。

   timer.schedule(task,10);//只執行一次task任務。

10.同步代碼塊

    爲了保證每個線程能正常執行原子操作,Java引入了同步機制,具體做法是在代表原子操作的程序代碼前加上synchronized標記,這樣的代碼被稱爲同步代碼塊。

    有兩種方式加代碼鎖:

1)  直接在方法前加synchronized標記,

public synchronized void add(){... }

2) 在方法內形成synchronized標記的代碼塊

   public void add(){synchronized(this){... }}

取得對象鎖:

1)  假如這個鎖已經被其他線程佔用,JVM就會把這個消費者線程放到Stack對象的鎖池中,消費者線程進入阻塞狀態。在Stack對象的鎖池中可能會有許多等待鎖的線程。等到其他線程釋放了鎖,JVM會從鎖池中隨機取出一個線程,使這個線程擁有鎖,並且轉到就緒狀態。

2)  假如這個鎖沒有被其他線程佔用,消費者線程就會獲得這把鎖,開始執行同步代碼塊。在一般情況下,消費者線程只有執行完同步代碼塊,纔會釋放鎖,使得其他線程能夠獲得鎖。

釋放對象鎖:

1)  執行完同步代碼塊,就會釋放鎖。

2)  在執行同步代碼塊的過程中,遇到異常而導致線程終止,鎖也會被釋放。

3)  在執行同步代碼塊的過程中,執行了鎖所屬對象的wait()方法,這個線程會釋放鎖,進入對象的等待池。

4)  在執行同步代碼塊的過程中,執行了鎖所屬對象的notify()方法後,JVM會從對象的等待池中隨機選擇一個線程,把它轉到對象的鎖池中。

線程同步的特徵:

1)  如果一個同步代碼塊和非同步代碼塊同時操縱共享資源,仍然會造成對共享資源的競爭。因爲當一個線程執行一個對象的同步代碼塊時,其他線程仍然可以執行對象的非同步代碼塊。

2)  每一個對象都有惟一的同步鎖。

3)  在靜態方法前面也可以使用synchronized修飾符。

4)  當一個線程開始執行同步代碼塊時,並不意味着必須以不中斷的方式運行。進入同步代碼塊中的線程也可以執行Thread.sleep()或者執行Thread.yield()方法,此時它並沒有釋放鎖,只是把運行機會(即CPU)讓給了其他的線程。

5)  synchronized聲明不會被繼承.

11.死鎖:

    當一個線程等等由另一個線程持有的鎖,而後者正在等待已被第一個線程持有的鎖時,就會發生死鎖。JVM不監測也不試圖避免這種情況,因此保證不發生死鎖就成了程序員的責任。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章