Java多線程

Java多線程html

整理轉載自:java

http://blog.csdn.net/evankaka編程

http://www.javashuo.com/article/p-ccdmtvsq-cz.html安全

http://www.runoob.com/java/java-multithreading.html多線程

目錄dom

1、    線程和進程    1 ide

2、    繼承Thread類和實現Runnable實現多線程    2函數

1.    繼承Thread    2this

2.    實現Runnable接口    4spa

3、    ThreadRunnable的區別    6

4、    線程狀態轉換    6

1.    新建狀態    6

2.    就緒狀態    7

3.    運行狀態    7

4.    阻塞狀態    7

5、    線程的優先級    8

1.    線程優先級    8

2.    線程睡眠sleep()    9

3.    線程等待wait()    9

4.    線程讓步yield()    9

5.    線程加入join()    9

6、    經常使用函數說明    9

1.    sleep(long millis):    9

2.    join():    9

3.    yield():    9

4.    sleep()yield()的區別    10

7、    常見線程名詞解釋    10

8、    線程同步    11

1.    synchronized關鍵字的做用域有兩種:    11

2.    如何使用synchronized同步解決多線程共享數據同步問題    12

3.    討論synchronized用到不一樣地方對代碼產生的影響:    12

4.    線程同步的目的    13

5.    synchronized, wait, notify結合:典型場景生產者消費者問題    14

6.    多線程的內存模型:    15

 

 

  1. 線程和進程

    首先咱們先了解下在操做系統中進程和線程的區別:

    1. 進程:每一個進程都有獨立的代碼和數據空間,進程間的切換會有較大的開銷,一個進程包含多個線程(進程時資源分配的最小單位)
    2. 線程:同一類線程共享代碼和數據空間,每一個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。(線程時CPU調度的最小單位)
    3. 線程和進程同樣都分爲五個階段:建立、就緒、運行、阻塞、終止。
    4. 多進程是指操做系統能同時運行多個任務(程序)
    5. 多線程是指在同一個程序中有多個順序流在執行。

    Java想要實現多線程,有兩種方法,一種是繼承Thread類,另外一種是實現Runnable接口。

  2. 繼承Thread類和實現Runnable實現多線程
    1. 繼承Thread

class Thread1 extends Thread{

public Thread1(){

//重寫類的構造方法

}

public void run(){

//重寫父類run方法

}

}

public class Main {

 

public static void main(String[] args) {

Thread1 mTh1=new Thread1("A");

Thread1 mTh2=new Thread1("B");

mTh1.start();

mTh2.start();

 

}

 

}

輸出:

A運行 : 0

B運行 : 0

A運行 : 1

A運行 : 2

A運行 : 3

A運行 : 4

B運行 : 1

B運行 : 2

B運行 : 3

B運行 : 4

再運行一下:

A運行 : 0

B運行 : 0

B運行 : 1

B運行 : 2

B運行 : 3

B運行 : 4

A運行 : 1

A運行 : 2

A運行 : 3

A運行 : 4

程序啓動運行main()的時候,Java虛擬機會啓動一個進程,主線程mainmain(0函數調用時候被建立,隨着調用Main類的兩個對象的start()方法,另外兩個線程也被啓動,這樣,整個應用程序就在多線程下運行。

線程對象只會調用start()方法,不會調用run()方法,run()方法的執行是由操做系統決定的。

注意:start()方法調用後並非當即執行多線程代碼,而是使得該線程變爲可運行態(Runnable),何時運行是由操做系統決定的。

從程序運行的結果能夠發現,多線程程序是亂序執行的,只有亂序執行的代碼纔有必要設計爲多線程。

Thread.sleep()方法調用的目的是不讓當前線程獨自霸佔該進程所獲取的CPU資源,以留出必定時間給其餘線程執行的機會。

實際上,全部的多線程代碼的執行順序都是不肯定的,每次執行的結果都是隨機的。

可是start方法重複調用的話,會出現java.lang.IllegalThreadStateException異常。

  1. 實現Runnable接口

    採用實現Runnable接口也是建立多線程的一種方法

class Thread2 implements Runnable{

private String name;

 

public Thread2(String name) {

this.name=name;

}

 

@Override

public void run() {

for (int i = 0; i < 5; i++) {

System.out.println(name + "運行 : " + i);

try {

Thread.sleep((int) Math.random() * 10);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

 

}

 

}

public class Main {

 

public static void main(String[] args) {

new Thread(new Thread2("C")).start();

new Thread(new Thread2("D")).start();

}

 

}

輸出:

 

 

 

C運行 : 0

D運行 : 0

D運行 : 1

C運行 : 1

D運行 : 2

C運行 : 2

D運行 : 3

C運行 : 3

D運行 : 4

C運行 : 4

Thread2類經過實現Runnable接口,使得該類有了多線程類的特徵。run()方法是多線程程序的一個約定。全部的多線程代碼都在run方法裏面。Thread類實際上也是實現了Runnable接口的類。

在啓動的多線程的時候,須要先經過Thread類的構造方法Thread(Runnable target) 構造出對象,而後調用Thread對象的start()方法來運行多線程代碼。

實際上全部的多線程代碼都是經過運行Threadstart()方法來運行的。所以,無論是擴展Thread類仍是實現Runnable接口來實現多線程,最終仍是經過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程編程的基礎。

  1. ThreadRunnable的區別

    若是一個類繼承Thread,則不適合資源共享。可是若是實現了Runable接口的話,則很容易的實現資源共享。

總結:

實現Runnable接口比繼承Thread類所具備的優點:

1):適合多個相同的程序代碼的線程去處理同一個資源

2):能夠避免java中的單繼承的限制

3):增長程序的健壯性,代碼能夠被多個線程共享,代碼和數據獨立

4):線程池只能放入實現Runablecallable類線程,不能直接放入繼承Thread的類

提醒一下你們:main方法其實也是一個線程。在java中因此的線程都是同時啓動的,至於何時,哪一個先執行,徹底看誰先獲得CPU的資源。

java中,每次程序運行至少啓動2個線程。一個是main線程,一個是垃圾收集線程。由於每當使用java命令執行一個類的時候,實際上都會啓動一個JVM,每個jVM實習在就是在操做系統中啓動了一個進程。

  1. 線程狀態轉換
    1. 新建狀態

      使用new關鍵字和Thread類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序start()這個線程。

       

    2. 就緒狀態

      當線程調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。

       

    3. 運行狀態

      若是就緒狀態的線程獲取CPU資源,就能夠執行run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。

       

    4. 阻塞狀態

      若是一個線程執行了sleep()休眠、suspend()掛起等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲如下三種:

  • 等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態,運行的線程執行wait()方法,JVM會把該線程放入等待池中。(wait會釋放持有的鎖)只有當同一對象的其餘進程執行notify()時纔會被釋放。
  • 同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用) JVM會把該線程放入鎖池中。。
  • 其餘阻塞:經過調用線程的 sleep() join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。

    Sleep()不會釋放持有的鎖,只會超時使得線程終止。

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

  1. 線程的優先級
    1. 線程優先級

    每個 Java 線程都有一個優先級,這樣有助於操做系統肯定線程的調度順序。Java 線程的優先級是一個整數,其取值範圍是 1 Thread.MIN_PRIORITY - 10 Thread.MAX_PRIORITY )。

    線程能夠具備的最高優先級,取值爲10

    線程能夠具備的最低優先級,取值爲1

默認狀況下,每個線程都會分配一個優先級 NORM_PRIORITY5)。

具備較高優先級的線程對程序更重要,而且應該在低優先級的線程以前分配處理器資源。可是,線程優先級不能保證線程執行的順序,並且很是依賴於平臺。

  1. 線程睡眠sleep()

    Thread.sleep(long millis)方法,使線程轉到阻塞狀態。millis參數設定睡眠的時間,以毫秒爲單位。當睡眠結束後,就轉爲就緒(Runnable)狀態

  2. 線程等待wait()

    線程等待:Object類中的wait()方法,致使當前的線程等待,直到其餘線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法。

  3. 線程讓步yield()

    線程讓步:Thread.yield() 方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先級的線程。

  4. 線程加入join()

    線程加入:join()方法,等待其餘線程終止。在當前線程中調用另外一個線程的join()方法,則當前線程轉入阻塞狀態,直到另外一個進程運行結束,當前線程再由阻塞轉爲就緒狀態。

  1. 經常使用函數說明
    1. sleep(long millis):

      在指定的毫秒數內讓當前正在執行的線程休眠(暫停運行)

    2. join():

      指等待t線程終止。該線程是指的主線程等待子線程的終止。有時主線程須要等待子線程執行完成以後再結束,這個時候就要用到join()方法了。

    3. yield():

      暫停當前正在執行的線程對象,並執行其餘線程。

      yield()應該作的是讓當前運行線程回到可運行狀態,以容許具備相同優先級的其餘線程得到運行機會。

      yield()不會致使線程轉到等待/睡眠/阻塞狀態

    4. sleep()yield()的區別
    5. sleep()使當前線程進入停滯狀態,因此執行sleep()的線程在指定的時間內確定不會被執行;yield()只是使當前線程從新回到可執行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行。
    6. sleep 方法容許較低優先級的線程得到運行機會;但 yield() 方法執行時,當前線程仍處在可運行狀態,因此,不可能讓出較低優先級的線程些時得到 CPU 佔有權。
    7. sleep 方法使當前運行中的線程睡眼一段時間,進入不可運行狀態,這段時間的長短是由程序設定的;yield 方法使當前線程讓出 CPU 佔有權,但讓出的時間是不可設定的。實際上,yield()方法對應了以下操做:先檢測當前是否有相同優先級的線程處於同可運行狀態,若有,則把 CPU 的佔有權交給此線程,不然,繼續運行原來的線程。因此yield()方法稱爲"退讓",它把運行機會讓給了同等優先級的其餘線程

 

  1. 常見線程名詞解釋
    1. 主線程:JVM調用程序main()所產生的線程。
    2. 當前線程:這個是容易混淆的概念。通常指經過Thread.currentThread()來獲取的進程。
    3. 後臺線程:指爲其餘線程提供服務的線程,也稱爲守護線程。JVM的垃圾回收線程就是一個後臺線程。用戶線程和守護線程的區別在於,是否等待主線程依賴於主線程結束而結束
    4. 前臺線程:是指接受後臺線程服務的線程,其實前臺後臺線程是聯繫在一塊兒,就像傀儡和幕後操縱者同樣的關係。傀儡是前臺線程、幕後操縱者是後臺線程。由前臺線程建立的線程默認也是前臺線程。能夠經過isDaemon()setDaemon()方法來判斷和設置一個線程是否爲後臺線程。
  2. 線程同步
    1. synchronized關鍵字的做用域有兩種:
      1. 是某個對象實例內,synchronized aMethod(){}能夠防止多個線程同時訪問這個對象的synchronized方法(若是一個對象有多個synchronized方法,只要一個線程訪問了其中的一個synchronized方法,其它線程不能同時訪問這個對象中任何一個synchronized方法)。這時,不一樣的對象實例的synchronized方法是不相干擾的。也就是說,其它線程照樣能夠同時訪問相同類的另外一個對象實例中的synchronized方法;
      2. 是某個類的範圍,synchronized static aStaticMethod{}防止多個線程同時訪問這個類中的synchronized static 方法。它能夠對類的全部對象實例起做用。
      3. synchronized關鍵字是不能繼承的,也就是說,基類的方法synchronized f(){} 在繼承類中並不自動是synchronized f(){},而是變成了f(){}。繼承類須要你顯式的指定它的某個方法爲synchronized方法;
    2. 如何使用synchronized同步解決多線程共享數據同步問題

      在進一步闡述以前,咱們須要明確幾點:

      A.不管synchronized關鍵字加在方法上仍是對象上,它取得的鎖都是對象,而不是把一段代碼或函數看成鎖――並且同步方法極可能還會被其餘線程的對象訪問。

    B.每一個對象只有一個鎖(lock)與之相關聯。

    C.實現同步是要很大的系統開銷做爲代價的,甚至可能形成死鎖,因此儘可能避免無謂的同步控制。

    1. 討論synchronized用到不一樣地方對代碼產生的影響:
      1. synchronized看成函數修飾符時,示例代碼以下:

Public synchronized void methodAAA()

{

//….

}

這也就是同步方法,那這時synchronized鎖定的是哪一個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象P1在不一樣的線程中執行這個同步方法時,它們之間會造成互斥,達到同步的效果。可是這個對象所屬的Class所產生的另外一對象P2卻能夠任意調用這個被加了synchronized關鍵字的方法。

  1. 同步塊,示例代碼以下:

public void method3(SomeObject so){

synchronized(so){

//…..

}

}

這時,鎖就是so這個對象,誰拿到這個鎖誰就能夠運行它所控制的那段代碼。當有一個明確的對象做爲鎖時,就能夠這樣寫程序。

  1. synchronized做用於static 函數,示例代碼以下:

Class Foo

{

public synchronized static void methodAAA()

{

//….

}

public void methodBBB()

{

synchronized(Foo.class) // class literal(類名稱字面常量)

}

}

代碼中的methodBBB()方法是把class literal做爲鎖的狀況,它和同步的static函數產生的效果是同樣的,取得的鎖很特別,是當前調用這個方法的對象所屬的類(Class,而再也不是由這個Class產生的某個具體對象了)。

  1. 線程同步的目的
    1. 線程同步的目的是爲了保護多個線程訪問一個資源時對資源的破壞
    2. 線程同步方法是經過鎖來實現,每一個對象都有切僅有一個鎖,這個鎖與一個特定的對象關聯,線程一旦獲取了對象鎖,其餘訪問該對象的線程就沒法再訪問該對象的其餘非同步方法。
    3. 對於靜態同步方法,鎖是針對這個類的,鎖對象是該類的Class對象。靜態和非靜態方法的鎖互不干預。一個線程得到鎖,當在一個同步方法中訪問另外對象上的同步方法時,會獲取這兩個對象鎖。
    4. 對於同步,要時刻清醒在哪一個對象上同步,這是關鍵。
    5. 編寫線程安全的類,須要時刻注意對多個線程競爭訪問資源的邏輯和安全作出正確的判斷,對"原子"操做作出分析,並保證原子操做期間別的線程沒法訪問競爭資源。
    6. 當多個線程等待一個對象鎖時,沒有獲取到鎖的線程將發生阻塞。
    7. 死鎖是線程間相互等待鎖鎖形成的,在實際中發生的機率很是的小。真讓你寫個死鎖程序,不必定好使,呵呵。可是,一旦程序發生死鎖,程序將死掉。
  2. synchronized, wait, notify結合:典型場景生產者消費者問題

public synchronized void consume()

{

if(this.product <= MIN_PRODUCT)

{

try

{

wait();

System.out.println("缺貨,稍候再取");

}

catch (InterruptedException e)

{

e.printStackTrace();

}

return;

}

System.out.println("消費者取走了第" + this.product + "個產品.");

this.product--;

notifyAll(); //通知等待去的生產者能夠生產產品了

}

 

  1. 多線程的內存模型:

    main memory(主存)、working memory(線程棧),在處理數據時,線程會把值從主存load到本地棧,完成操做後再save回去(volatile關鍵詞的做用:每次針對該變量的操做都激發一次load and save)

相關文章
相關標籤/搜索