Java多執行緒程式設計入門(轉)

ba發表於2007-08-15
Java多執行緒程式設計入門(轉)[@more@]在Java語言產生前,傳統的程式設計語言的程式同一時刻只能單任務操作,效率非常低,例如程式往往在接收資料輸入時發生阻塞,只有等到程式獲得資料後才能繼續執行。隨著Internet的迅猛發展,這種狀況越來越不能讓人們忍受:如果網路接收資料阻塞,後臺程式就處於等待狀態而不繼續任何操作,而這種阻塞是經常會碰到的,此時CPU資源被白白的閒置起來。如果在後臺程式中能夠同時處理多個任務,該多好啊!應Internet技術而生的Java語言解決了這個問題,多執行緒程式是Java語言的一個很重要的特點。在一個Java程式中,我們可以同時並行執行多個相對獨立的執行緒,例如,我們如果建立一個執行緒來進行資料輸入輸出,而建立另一個執行緒在後臺進行其它的資料處理,如果輸入輸出執行緒在接收資料時阻塞,而處理資料的執行緒仍然在執行。多執行緒程式設計大大提高了程式執行效率和處理能力。


  執行緒的建立


  我們知道Java是物件導向的程式語言,用Java進行程式設計就是設計和使用類,Java為我們提供了執行緒類Thread來建立執行緒,建立執行緒與建立普通的類的物件的操作是一樣的,而執行緒就是Thread類或其子類的例項物件。下面是一個建立啟動一個執行緒的語句:


  Thread thread1=new Thread(); file://宣告一個物件例項,即建立一個執行緒;


  Thread1.run(); file://用Thread類中的run()方法啟動執行緒;


  從這個例子,我們可以透過Thread()構造方法建立一個執行緒,並啟動該執行緒。事實上,啟動執行緒,也就是啟動執行緒的run()方法,而 Thread類中的run()方法沒有任何操作語句,所以這個執行緒沒有任何操作。要使執行緒實現預定功能,必須定義自己的run()方法。Java中通常有兩種方式定義run()方法:


  透過定義一個Thread類的子類,在該子類中重寫run()方法。Thread子類的例項物件就是一個執行緒,顯然,該執行緒有我們自己設計的執行緒體run()方法,啟動執行緒就啟動了子類中重寫的run()方法。


  透過Runnable介面,在該介面中定義run()方法的介面。所謂介面跟類非常類似,主要用來實現特殊功能,如複雜關係的多重繼承功能。在此,我們定義一個實現Runnable() 介面的類,在該類中定義自己的run()方法,然後以該類的例項物件為引數呼叫Thread類的構造方法來建立一個執行緒。


  執行緒被實際建立後處於待命狀態,啟用(啟動)執行緒就是啟動執行緒的run()方法,這是透過呼叫執行緒的start()方法來實現的。


  下面一個例子實踐瞭如何透過上述兩種方法建立執行緒並啟動它們:


  // 透過Thread類的子類建立的執行緒;


   class thread1 extends Thread


    { file://自定義執行緒的run()方法;


     public void run()


      {


       System.out.println("Thread1 is running…");


      }


     }


   file://透過Runnable介面建立的另外一個執行緒;


  class thread2 implements Runnable


   { file://自定義執行緒的run()方法;


    public void run()


    {


     System.out.println("Thread2 is running…");


    }


   }


   file://程式的主類´


   class Multi_Thread file://宣告主類;


    {


     plubic static void mail(String args[]) file://宣告主方法;


      {


       thread1 threadone=new thread1(); file://用Thread類的子類建立執行緒;


       Thread threadtwo=new Thread(new thread2()); file://用Runnable介面類的物件建立執行緒;


       threadone.start(); threadtwo.start(); file://strat()方法啟動執行緒;


      }


     }


  執行該程式就可以看出,執行緒threadone和threadtwo交替佔用CPU,處於並行執行狀態。可以看出,啟動執行緒的run()方法是透過呼叫執行緒的start()方法來實現的(見上例中主類),呼叫start()方法啟動執行緒的run()方法不同於一般的呼叫方法,呼叫一般方法時,必須等到一般方法執行完畢才能夠返回start()方法,而啟動執行緒的run()方法後,start()告訴系統該執行緒準備就緒可以啟動run()方法後,就返回start()方法執行呼叫start()方法語句下面的語句,這時run()方法可能還在執行,這樣,執行緒的啟動和執行並行進行,實現了多工操作。


  執行緒的優先順序


  對於多執行緒程式,每個執行緒的重要程度是不盡相同,如多個執行緒在等待獲得CPU時間時,往往我們需要優先順序高的執行緒優先搶佔到CPU時間得以執行;又如多個執行緒交替執行時,優先順序決定了級別高的執行緒得到CPU的次數多一些且時間多長一些;這樣,高優先順序的執行緒處理的任務效率就高一些。


  Java中執行緒的優先順序從低到高以整數1~10表示,共分為10級,設定優先順序是透過呼叫執行緒物件的setPriority()方法,如上例中,設定優先順序的語句為:


  thread1 threadone=new thread1(); file://用Thread類的子類建立執行緒;
  Thread threadtwo=new Thread(new thread2()); file://用Runnable介面類的物件建立執行緒;


  threadone.setPriority(6); file://設定threadone的優先順序6;


  threadtwo.setPriority(3); file://設定threadtwo的優先順序3;


  threadone.start(); threadtwo.start(); file://strat()方法啟動執行緒;


  這樣,執行緒threadone將會優先於執行緒threadtwo執行,並將佔有更多的CPU時間。該例中,優先順序設定放線上程啟動前,也可以在啟動後進行設定,以滿足不同的優先順序需求。


  執行緒的(同步)控制


  一個Java程式的多執行緒之間可以共享資料。當執行緒以非同步方式訪問共享資料時,有時候是不安全的或者不和邏輯的。比如,同一時刻一個執行緒在讀取資料,另外一個執行緒在處理資料,當處理資料的執行緒沒有等到讀取資料的執行緒讀取完畢就去處理資料,必然得到錯誤的處理結果。這和我們前面提到的讀取資料和處理資料並行多工並不矛盾,這兒指的是處理資料的執行緒不能處理當前還沒有讀取結束的資料,但是可以處理其它的資料。


  如果我們採用多執行緒同步控制機制,等到第一個執行緒讀取完資料,第二個執行緒才能處理該資料,就會避免錯誤。可見,執行緒同步是多執行緒程式設計的一個相當重要的技術。


  在講執行緒的同步控制前我們需要交代如下概念:


  1 用Java關鍵字synchonized同步對共享資料操作的方法


  在一個物件中,用synchonized宣告的方法為同步方法。Java中有一個同步模型-監視器,負責管理執行緒對物件中的同步方法的訪問,它的原理是:賦予該物件唯一一把´鑰匙´,當多個執行緒進入物件,只有取得該物件鑰匙的執行緒才可以訪問同步方法,其它執行緒在該物件中等待,直到該執行緒用 wait()方法放棄這把鑰匙,其它等待的執行緒搶佔該鑰匙,搶佔到鑰匙的執行緒後才可得以執行,而沒有取得鑰匙的執行緒仍被阻塞在該物件中等待。
  file://宣告同步的一種方式:將方法宣告同步


  class store


   {


    public synchonized void store_in()


    {
     ….


    }
    public synchonized void store_out(){


       ….}
    }


  2 利用wait()、notify()及notifyAll()方法傳送訊息實現執行緒間的相互聯絡


  Java程式中多個執行緒透過訊息來實現互動聯絡的,這幾種方法實現了執行緒間的訊息傳送。例如定義一個物件的synchonized 方法,同一時刻只能夠有一個執行緒訪問該物件中的同步方法,其它執行緒被阻塞。通常可以用notify()或notifyAll()方法喚醒其它一個或所有執行緒。而使用wait()方法來使該執行緒處於阻塞狀態,等待其它的執行緒用notify()喚醒。


  一個實際的例子就是生產和銷售,生產單元將產品生產出來放在倉庫中,銷售單元則從倉庫中提走產品,在這個過程中,銷售單元必須在倉庫中有產品時才能提貨;如果倉庫中沒有產品,則銷售單元必須等待。


  程式中,假如我們定義一個倉庫類store,該類的例項物件就相當於倉庫,在store類中定義兩個成員方法:store_in(),用來模擬產品製造者往倉庫中新增產品;strore_out()方法則用來模擬銷售者從倉庫中取走產品。然後定義兩個執行緒類:customer類,其中的run ()方法透過呼叫倉庫類中的store_out()從倉庫中取走產品,模擬銷售者;另外一個執行緒類producer中的run()方法透過呼叫倉庫類中的 store_in()方法向倉庫新增產品,模擬產品製造者。在主類中建立並啟動執行緒,實現向倉庫中新增產品或取走產品。


  如果倉庫類中的store_in() 和store_out()方法不宣告同步,這就是個一般的多執行緒,我們知道,一個程式中的多執行緒是交替執行的,執行也是無序的,這樣,就可能存在這樣的問題:


  倉庫中沒有產品了,銷售者還在不斷光顧,而且還不停的在´取´產品,這在現實中是不可思義的,在程式中就表現為負值;如果將倉庫類中的 stroe_in()和store_out()方法宣告同步,如上例所示:就控制了同一時刻只能有一個執行緒訪問倉庫物件中的同步方法;即一個生產類執行緒訪問被宣告為同步的store_in()方法時,其它執行緒將不能夠訪問物件中的store_out()同步方法,當然也不能訪問store_in()方法。必須等到該執行緒呼叫wait()方法放棄鑰匙,其它執行緒才有機會訪問同步方法。


  這個原理實際中也很好理解,當生產者(producer)取得倉庫唯一的鑰匙,就向倉庫中添放產品,此時其它的銷售者(customer,可以是一個或多個)不可能取得鑰匙,只有當生產者添放產品結束,交還鑰匙並且通知銷售者,不同的銷售者根據取得鑰匙的先後與否決定是否可以進入倉庫中提走產品。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-958503/,如需轉載,請註明出處,否則將追究法律責任。

相關文章