java多執行緒程式設計--基礎篇

九天高遠發表於2013-08-22

一、基本概念

a、作業系統中程式與執行緒的概念

現在的作業系統是多工作業系統。多執行緒是實現多工的一種方式。 程式是指一個記憶體中執行的應用程式,每個程式都有自己獨立的一塊記憶體空間,

一個程式中可以啟動多個執行緒。比如在Windows系統中,一個執行的exe就是一個程式。

執行緒是指程式中的一個執行流程,一個程式中可以執行多個執行緒。比如java.exe程式中可以執行很多執行緒。執行緒總是屬於某個程式,程式中的多個執行緒共享程式的記憶體空間。

“同時”執行是人的感覺,線上程之間實際上輪換執行。

b、Java中的執行緒 在Java中,“執行緒”指兩件不同的事情:       

           a、java.lang.Thread類的一個例項。       

           b、執行緒的執行。

使用java.lang.Thread類或者java.lang.Runnable介面編寫程式碼來定義、例項化和啟動新執行緒。

一個Thread類例項只是一個物件,像Java中的任何其他物件一樣,具有變數和方法,在堆上完成物件的建立和銷燬。

Java中,每個執行緒都有一個呼叫棧,即使不在程式中建立任何新的執行緒,執行緒也在後臺執行著。

一個Java應用總是從main()方法開始執行,mian()方法執行在一個執行緒內,它被稱為主執行緒。

一旦建立一個新的執行緒,就產生一個新的呼叫棧。執行緒總體分兩類:使用者執行緒守護執行緒

當所有使用者執行緒執行完畢的時候,JVM自動關閉。但是守護執行緒卻獨立於JVM,守護執行緒一般是由作業系統或者使用者自己建立的。

二、執行緒的建立與啟動

a、定義執行緒

1、繼承java.lang.Thread類。

此類中有個run()方法,應該注意其用法: public void run() 如果該執行緒是使用獨立的 Runnable 執行物件構造的,則呼叫該 Runnable 物件的 run 方法;否則,該方法不執行任何操作並返回。  Thread 的子類應該重寫該方法。

2、實現java.lang.Runnable介面。

void run() 使用實現介面 Runnable 的物件建立一個執行緒時,啟動該執行緒將導致在獨立執行的執行緒中呼叫物件的 run 方法。  

方法 run 的常規協定是,它可能執行任何所需的操作。

b、例項化執行緒

1、如果是繼承java.lang.Thread類的執行緒,則直接new即可。

即使用的時候Thread a=new tThread();

2、如果是實現了java.lang.Runnable介面的類,則用Thread的構造方法:

Thread(Runnable target) 
Thread(Runnable target, String name) 
Thread(ThreadGroup group, Runnable target) 
Thread(ThreadGroup group, Runnable target, String name) 
Thread(ThreadGroup group, Runnable target, String name, long stackSize) 

 

rThread a=new rThread();//此處可以使用各個建構函式
Thread b=new Thread(a);

 

c、啟動執行緒

        線上程的Thread物件上呼叫start()方法,而不是run()或者別的方法。 在呼叫start()方法之前:執行緒處於新狀態中,新狀態指有一個Thread物件,但還沒有一個真正的執行緒。 在呼叫start()方法之後,發生了一系列複雜的事情:啟動新的執行執行緒(具有新的呼叫棧)該執行緒從新狀態轉移到可執行狀態當該執行緒獲得機會執行時,其目標run()方法將執行

start()方法後,run()方法並沒有執行。

注意:對Java來說,run()方法沒有任何特別之處。像main()方法一樣,它只是新執行緒知道呼叫的方法名稱(和簽名)。因此,在Runnable上或者Thread上呼叫run方法是合法的,但並不啟動新的執行緒,所以通常不使用

d、基本例子

 1、實現Runnable介面的多執行緒例子

package com.yunhe.thread;
/**
 * 實現Runable介面的類
 * */
public class DoSomething implements Runnable {
    private String name;

    public DoSomething(String name) {
        this.name = name;
    }

    public void run() {
        for (int i = 0; i < 5; i++) {
            for (long k = 0; k < 100000000; k++);
            System.out.println(name + ": " + i);
        }
    }
}

測試類程式碼:

package com.yunhe.thread;
/**
 * 實現Runnable的執行緒的測試類 
*/ public class TestRunnable { public static void main(String[] args) { DoSomething ds1=new DoSomething("tianti"); DoSomething ds2=new DoSomething("yunhe"); Thread t1=new Thread(ds1); Thread t2=new Thread(ds2); t1.start(); t2.start(); } }

執行結果:

tianti: 0
yunhe: 0
yunhe: 1
tianti: 1
yunhe: 2
tianti: 2
yunhe: 3
tianti: 3
yunhe: 4
tianti: 4

2、繼承Thread實現執行緒的例子

package com.yunhe.thread;
/**
 * 繼承Thread的執行緒類
 * */
public class DoOtherthing extends Thread {
    public DoOtherthing(String name) {
        super(name);
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        for (int i = 0; i < 5; i++) {
            for (long k = 0; k < 1000000000; k++);
            System.out.println(this.getName() + ": " + i);
        }
    }
   
}

測試類程式碼:

package com.yunhe.thread;
/**
 * 繼承Thread類
 * */
public class DoOtherthing extends Thread {
    public DoOtherthing(String name) {
        super(name);
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        for (int i = 0; i < 5; i++) {
            for (long k = 0; k < 1000000000; k++);
            System.out.println(this.getName() + ": " + i);
        }
    }
   
}

執行結果:

tianti: 0
yunhe: 0
tianti: 1
yunhe: 1
tianti: 2
yunhe: 2
tianti: 3
yunhe: 3
tianti: 4
yunhe: 4

對於上面的多執行緒程式程式碼來說,輸出的結果是不確定的。

其中for (long k = 0; k < 100000000; k++);是模擬非常耗時的操作,k值上限較小的時候輸出是順序的,較大的時候是不穩定的交叉輸出。

e、一些常見的問題

1、執行緒的名字,一個執行中的執行緒總是有名字的,名字有兩個來源,一個是JVM虛擬機器自己給的名字,一個是你自己的定的名字。在沒有指定執行緒名字的情況下,JVM虛擬機器總會為執行緒指定名字,並且主執行緒的名字總是main,非主執行緒的名字不確定。

2、執行緒都可以設定名字,也可以獲取執行緒的名字,連主執行緒也不例外。

3、獲取當前執行緒的物件的方法是:Thread.currentThread();

4、在上面的程式碼中,只能保證:每個執行緒都將啟動,每個執行緒都將執行直到完成。一系列執行緒以某種順序啟動並不意味著將按該順序執行。對於任何一組啟動的執行緒來說,排程程式不能保證其執行次序,持續時間也無法保證

5、當執行緒目標run()方法結束時該執行緒完成。

6、一旦執行緒啟動,它就永遠不能再重新啟動。只有當執行緒是新的執行緒的時候才可以被啟動,並且只能一次。一個可執行的執行緒(就緒狀態)或死執行緒可以被重新啟動。

7、執行緒的排程是JVM的一部分,在一個CPU的機器上上,實際上一次只能執行一個執行緒。一次只有一個執行緒棧執行。JVM執行緒排程程式決定實際執行哪個處於可執行狀態的執行緒(就緒狀態)。

眾多可執行執行緒中的某一個會被選中做為當前執行緒。可執行執行緒被選擇執行的順序是沒有保障的。

8、儘管通常採用佇列形式,但這是沒有保障的。佇列形式是指當一個執行緒完成“一輪”時,它移到可執行佇列的尾部等待,直到它最終排隊到該佇列的前端為止,它才能被再次選中。事實上,我們把它稱為可執行池而不是一個可執行佇列,目的是幫助認識執行緒執行並不都是以某種有保障的順序排列來排程執行緒。

9、儘管我們不能控制執行緒排程的順序,但可以透過別的方式來影響執行緒排程的方式。

 

 

 

相關文章