天天看點

JAVA中的線程以及與程序的差別

一、程序與應用程式的差別 

   

  程序(Process)是最初定義在Unix等多使用者、多任務作業系統環境下用于表示應用程式在記憶體環境中基本執行單元的概念。以Unix作業系統為例,程序是Unix作業系統環境中的基本成分、是系統資源配置設定的基本機關。Unix作業系統中完成的幾乎所有使用者管理和資源配置設定等工作都是通過作業系統對應用程式程序的控制來實作的。 

   

  C、C++、Java等語言編寫的源程式經相應的編譯器編譯成可執行檔案後,送出給計算機處理器運作。這時,處在可執行狀态中的應用程式稱為程序。從使用者角度來看,程序是應用程式的一個執行過程。從作業系統核心角度來看,程序代表的是作業系統配置設定的記憶體、CPU時間片等資源的基本機關,是為正在運作的程式提供的運作環境。程序與應用程式的差別在于應用程式作為一個靜态檔案存儲在計算機系統的硬碟等存儲空間中,而程序則是處于動态條件下由作業系統維護的系統資源管理實體。多任務環境下應用程式程序的主要特點包括: 

   

  ●程序在執行過程中有記憶體單元的初始入口點,并且程序存活過程中始終擁有獨立的記憶體位址空間; 

   

  ●程序的生存期狀态包括建立、就緒、運作、阻塞和死亡等類型; 

   

  ●從應用程式程序在執行過程中向CPU發出的運作指令形式不同,可以将程序的狀态分為使用者态和核心态。處于使用者态下的程序執行的是應用程式指令、處于核心态下的應用程式程序執行的是作業系統指令。 

   

  在Unix作業系統啟動過程中,系統自動建立swapper、init等系統程序,用于管理記憶體資源以及對使用者程序進行排程等。在Unix環境下無論是由作業系統建立的程序還要由應用程式執行建立的程序,均擁有唯一的程序辨別(PID)。

二、程序與Java線程的差別 

   

   

  應用程式在執行過程中存在一個記憶體空間的初始入口點位址、一個程式執行過程中的代碼執行序列以及用于辨別程序結束的記憶體出口點位址,在程序執行過程中的每一時間點均有唯一的處理器指令與記憶體單元位址相對應。 

   

  Java語言中定義的線程(Thread)同樣包括一個記憶體入口點位址、一個出口點位址以及能夠順序執行的代碼序列。但是程序與線程的重要差別在于線程不能夠單獨執行,它必須運作在處于活動狀态的應用程式程序中,是以可以定義線程是程式内部的具有并發性的順序代碼流。 

   

  Unix作業系統和Microsoft Windows作業系統支援多使用者、多程序的并發執行,而Java語言支援應用程式程序内部的多個執行線程的并發執行。多線程的意義在于一個應用程式的多個邏輯單元可以并發地執行。但是多線程并不意味着多個使用者程序在執行,作業系統也不把每個線程作為獨立的程序來配置設定獨立的系統資源。程序可以建立其子程序,子程序與父程序擁有不同的可執行代碼和資料記憶體空間。而在用于代表應用程式的程序中多個線程共享資料記憶體空間,但保持每個線程擁有獨立的執行堆棧和程式執行上下文(Context)。 

   

  基于上述差別,線程也可以稱為輕型程序 (Light Weight Process,LWP)。不同線程間允許任務協作和資料交換,使得在計算機系統資源消耗等方面非常廉價。

   

  線程需要作業系統的支援,不是所有類型的計算機都支援多線程應用程式。Java程式設計語言将線程支援與語言運作環境結合在一起,提供了多任務并發執行的能力。這就好比一個人在處理家務的過程中,将衣服放到洗衣機中自動洗滌後将大米放在電飯鍋裡,然後開始做菜。等菜做好了,飯熟了同時衣服也洗好了。 

   

  需要注意的是:在應用程式中使用多線程不會增加 CPU 的資料處理能力。隻有在多CPU 的計算機或者在網絡計算體系結構下,将Java程式劃分為多個并發執行線程後,同時啟動多個線程運作,使不同的線程運作在基于不同處理器的Java虛拟機中,才能提高應用程式的執行效率。

另外,如果應用程式必須等待網絡連接配接或資料庫連接配接等資料吞吐速度相對較慢的資源時,多線程應用程式是非常有利的。基于Internet的應用程式有必要是多線程類型的,例如,當開發要支援大量客戶機的伺服器端應用程式時,可以将應用程式建立成多線程形式來響應用戶端的連接配接請求,使每個連接配接使用者獨占一個用戶端連接配接線程。這樣,使用者感覺伺服器隻為連接配接使用者自己服務,進而縮短了伺服器的用戶端響應時間。 

   

   

三、Java語言的多線程程式設計方法 

   

   

  利用Java語言實作多線程應用程式的方法很簡單。根據多線程應用程式繼承或實作對象的不同可以采用兩種方式:一種是應用程式的并發運作對象直接繼承Java的線程類Thread;另外一種方式是定義并發執行對象實作Runnable接口。 

   

  繼承Thread類的多線程程式設計方法 

   

  Thread 類是JDK中定義的用于控制線程對象的類,在該類中封裝了用于進行線程控制的方法。見下面的示例代碼: 

   

  

[code]//Consumer.java 
   import java.util.*; 
   class Consumer extends Thread 
   { 
    int nTime; 
    String strConsumer; 
    public Consumer(int nTime, String strConsumer) 
    { 
    this.nTime = nTime; 
    this.strConsumer = strConsumer; 
    } 
    public void run() 
    { 
   while(true) 
   { 
    try 
   { 
    System.out.println("Consumer name:"+strConsumer+"\n"); 
    Thread.sleep(nTime); 
    } 
   catch(Exception e) 
   { 
    e.printStackTrace(); 
    } 
   } 
    } 
   static public void main(String args[]) 
   { 
    Consumer aConsumer = new Consumer (1000, "aConsumer"); 
    aConsumer.start(); 
    Consumer bConsumer = new Consumer (2000, "bConsumer"); 
    bConsumer.start(); 
    Consumer cConsumer = new Consumer (3000, "cConsumer "); 
    cConsumer.start(); 
   } 
   } [/code]
    
    
    
    
   從上面的程式代碼可以看出:多線程執行地下Consumer繼承Java語言中的線程類Thread并且在main方法中建立了三個Consumer對象的執行個體。當調用對象執行個體的start方法時,自動調用Consumer類中定義的run方法啟動對象線程運作。線程運作的結果是每間隔nTime時間列印出對象執行個體中的字元串成員變量strConsumer的内容。 
    
   可以總結出繼承Thread類的多線程程式設計方法是使應用程式類繼承Thread類并且在該類的run方法中實作并發性處理過程。 
    
   實作Runnable接口的多線程程式設計方法 
    
   Java語言中提供的另外一種實作多線程應用程式的方法是多線程對象實作Runnable接口并且在該類中定義用于啟動線程的run方法。這種定義方式的好處在于多線程應用對象可以繼承其它對象而不是必須繼承Thread類,進而能夠增加類定義的邏輯性。 
    
   實作Runnable接口的多線程應用程式架構代碼如下所示: 
    
   //Consumer.java 
   import java.util.*; 
   class Consumer implements Runnable 
   { 
    … … 
   public Consumer(int nTime, String strConsumer){… …} 
   public void run(){… …} 
   static public void main(String args[]) 
   { 
   Thread aConsumer = new Thread(new Consumer(1000, "aConsumer")); 
   aConsumer.start(); 
   //其它對象執行個體的運作線程 
    //… … 
    } 
   }      

   

  從上述代碼可以看出:該類實作了Runnable接口并且在該類中定義了run方法。這種多線程應用程式的實作方式與繼承Thread類的多線程應用程式的重要差別在于啟動多線程對象的方法設計方法不同。在上述代碼中,通過建立Thread對象執行個體并且将應用對象作為建立Thread類執行個體的參數。

四、線程間的同步 

   

  Java應用程式的多個線程共享同一程序的資料資源,多個使用者線程在并發運作過程中可能同時通路具有敏感性的内容。在Java中定義了線程同步的概念,實作對共享資源的一緻性維護。下面以筆者最近開發的移動通信計費系統中線程間同步控制方法,說明Java語言中多線程同步方式的實作過程。 

   

  在沒有多線程同步控制政策條件下的客戶賬戶類定義架構代碼如下所示: 

   

 

public class RegisterAccount 
   { 
   float fBalance; 
   //客戶繳費方法 
   public void deposit(float fFees){ fBalance += fFees; } 
   //通話計費方法 
   public void withdraw(float fFees){ fBalance -= fFees; } 
   … … 
   }      

   

   

  讀者也許會認為:上述程式代碼完全能夠滿足計費系統實際的需要。确實,在單線程環境下該程式确實是可靠的。但是,多程序并發運作的情況是怎樣的呢?假設發生這種情況:客戶在客戶服務中心進行繳費的同時正在利用移動通信裝置僅此通話,客戶通話結束時計費系統啟動計費程序,而同時服務中心的從業人員也送出繳費程序運作。讀者可以看到如果發生這種情況,對客戶賬戶的處理是不嚴肅的。 

   

  如何解決這種問題呢?很簡單,在RegisterAccount類方法定義中加上用于辨別同步方法的關鍵字synchronized。這樣,在同步方法執行過程中該方法涉及的共享資源(在上述代碼中為fBalance成員變量)将被加上共享鎖,以確定在方法運作期間隻有該方法能夠對共享資源進行通路,直到該方法的線程運作結束打開共享鎖,其它線程才能夠通路這些共享資源。在共享鎖沒有打開的時候其它通路共享資源的線程處于阻塞狀态。 

   

  進行線程同步政策控制後的RegisterAccount類定義如下面代碼所示: 

   

 

public class RegisterAccount 
   { 
   float fBalance; 
   public synchronized void deposit(float fFees){ fBalance += fFees; } 
   public synchronized void withdraw(float fFees){ fBalance -= fFees; } 
   … … 
   }      

   

  從經過線程同步機制定義後的代碼形式可以看出:在對共享資源進行通路的方法通路屬性關鍵字(public)後附加同步定義關鍵字synchronized,使得同步方法在對共享資源通路的時候,為這些敏感資源附加共享鎖來控制方法執行期間的資源獨占性,實作了應用系統資料資源的一緻性管理和維護。

繼續閱讀