1. 程式人生 > >Android開發 Handler Runnable和Thread之間的區別和聯絡 應用----------------

Android開發 Handler Runnable和Thread之間的區別和聯絡 應用----------------

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

在java中可有兩種方式實現多執行緒,一種是繼承Thread類,一種是實現Runnable介面;Thread類是在java.lang包中定義的。一個類只要繼承了Thread類同時覆寫了本類中的run()方法就可以實現多執行緒操作了,但是一個類只能繼承一個父類,這是此方法的侷限。

下面看例子:


  package org.thread.demo;  
  
  class
   MyThread 
  extends
   Thread{  
  
  private
   String name;  
  
  public
   MyThread(String name) {  
  
  super
  ();  
  
  this
  .name = name;  
  }  
public   void  run(){   for ( int  i= 0 ;i< 10 ;i++){   System.out.println( "執行緒開始:" + this .name+ ",i=" +i);   }   }   }   package  org.thread.demo;   public   class  ThreadDemo01 {  
public   static   void  main(String[] args) {   MyThread mt1= new  MyThread( "執行緒a" );   MyThread mt2= new  MyThread( "執行緒b" );   mt1.run();   mt2.run();   }   }

但是,此時結果很有規律,先第一個物件執行,然後第二個物件執行,並沒有相互執行。在JDK的文件中可以發現,一旦呼叫start()方法,則會通過JVM找到run()方法。下面啟動start()方法啟動執行緒:


  
  1. package org.thread.demo;  
  2. public class ThreadDemo01 {  
  3. public static void main(String[] args) {  
  4. MyThread mt1=new MyThread("執行緒a");  
  5. MyThread mt2=new MyThread("執行緒b");  
  6. mt1.start();  
  7. mt2.start();  
  8. }  
  9. }; 

這樣程式可以正常完成互動式執行。那麼為啥非要使用start();方法啟動多執行緒呢?

在JDK的安裝路徑下,src.zip是全部的java源程式,通過此程式碼找到Thread中的start()方法的定義,可以發現此方法中使用了private native void start0();其中native關鍵字表示可以呼叫作業系統的底層函式,那麼這樣的技術成為JNI技術(java Native Interface)

Runnable介面

在實際開發中一個多執行緒的操作很少使用Thread類,而是通過Runnable介面完成。


  
  1. public interface Runnable{  
  2. public void run();  

例子:


  
  1. package org.runnable.demo;  
  2. class MyThread implements Runnable{  
  3. private String name;  
  4. public MyThread(String name) {  
  5. this.name = name;  
  6. }
  7. public void run(){  
  8. for(int i=0;i<100;i++){  
  9. System.out.println("執行緒開始:"+this.name+",i="+i);  
  10. }  
  11. }  
  12. }; 

但是在使用Runnable定義的子類中沒有start()方法,只有Thread類中才有。此時觀察Thread類,有一個構造方法:public Thread(Runnable targer)此構造方法接受Runnable的子類例項,也就是說可以通過Thread類來啟動Runnable實現的多執行緒。(start()可以協調系統的資源):


  
  1. package org.runnable.demo;  
  2. import org.runnable.demo.MyThread;  
  3. public class ThreadDemo01 {  
  4. public static void main(String[] args) {  
  5. MyThread mt1=new MyThread("執行緒a");  
  6. MyThread mt2=new MyThread("執行緒b");  
  7. new Thread(mt1).start();  
  8. new Thread(mt2).start();  
  9. }  

兩種實現方式的區別和聯絡:

在程式開發中只要是多執行緒肯定永遠以實現Runnable介面為主,因為實現Runnable介面相比繼承Thread類有如下好處:

  • 避免點繼承的侷限,一個類可以繼承多個介面。
  • 適合於資源的共享

以賣票程式為例,通過Thread類完成:


  
  1. package org.demo.dff;  
  2. class MyThread extends Thread{  
  3. private int ticket=10;  
  4. public void run(){  
  5. for(int i=0;i<20;i++){  
  6. if(this.ticket>0){  
  7. System.out.println("賣票:ticket"+this.ticket--);  
  8. }  
  9. }  
  10. }  
  11. }; 

下面通過三個執行緒物件,同時賣票:


  
  1. package org.demo.dff;  
  2. public class ThreadTicket {  
  3. public static void main(String[] args) {  
  4. MyThread mt1=new MyThread();  
  5. MyThread mt2=new MyThread();  
  6. MyThread mt3=new MyThread();  
  7. mt1.start();//每個執行緒都各賣了10張,共賣了30張票  
  8. mt2.start();//但實際只有10張票,每個執行緒都賣自己的票  
  9. mt3.start();//沒有達到資源共享  
  10. }  

如果用Runnable就可以實現資源共享,下面看例子:


  
  1. package org.demo.runnable;  
  2. class MyThread implements Runnable{  
  3. private int ticket=10;  
  4. public void run(){  
  5. for(int i=0;i<20;i++){  
  6. if(this.ticket>0){  
  7. System.out.println("賣票:ticket"+this.ticket--);  
  8. }  
  9. }  
  10. }  
  11. }  
  12. package org.demo.runnable;  
  13. public class RunnableTicket {  
  14. public static void main(String[] args) {  
  15. MyThread mt=new MyThread();  
  16. new Thread(mt).start();//同一個mt,但是在Thread中就不可以,如果用同一  
  17. new Thread(mt).start();//個例項化物件mt,就會出現異常  
  18. new Thread(mt).start();  
  19. }  
  20. }; 

雖然現在程式中有三個執行緒,但是一共賣了10張票,也就是說使用Runnable實現多執行緒可以達到資源共享目的。

Runnable介面和Thread之間的聯絡:

public class Thread extends Object implements Runnable

發現Thread類也是Runnable介面的子類。


第二:

Thread是系統給你的資源,有了Thread你才有從CPU那裡得到可執行時間片的權力, Thread並不認識你的程式,不知道有test 這樣的類,因為編序員有千千萬,每個人命名都不一樣,想要做的事都不一樣, 所以 Thread只認識一個! 那就是Runnable 。 Thread認識Runnable 並且知道Runnable 裡面有一個run方法. 一旦呼叫Thread的start方法,Runnable 方法裡的run就會被Thread自動執行。 所以,當我們把我們的類繼承(這裡應該叫實現介面)自Runnable 的時候,我們的程式就是屬於Runnable 一個型別的了。 雖然是Runnable 的子類,但人家認識你爸爸,當然也知道了你。 Thread可以不管你內部有什麼情況,他只管你有run()方法就行了,他就調start讓你去執行run 所以我們在run裡面寫點東西,這樣就可以讓系統執行我們想要做的程式碼了。 是不是很通俗很易懂呢? 所以要執行執行緒的步驟是, 1。生成我們自己的類物件 2。從系統那裡得到Thread 3。讓Threa調我們的類物件,讓其start起來 程式碼: test a=new test(); Thread thread=new Thread(a); //Thread需要一個引數,就是你編的執行緒類,這樣他就認識了你的執行緒,也有資格向系統申請拿到CPU時間片thread.start(); 你可以簡單點寫: new Thread(a).start();

第三:

Runnable 並不一定是新開一個執行緒,比如下面的呼叫方法就是執行在UI主執行緒中的:

     Handler mHandler=new Handler();      mHandler.post(new Runnable(){         @Override public void run()         { // TODO Auto-generated method stub          }      });

官方對這個方法的解釋如下,注意其中的:“The runnable will be run on the user interface thread.

boolean android.view.View .post(Runnable action)

Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread.

Parameters:

action The Runnable that will be executed.

Returns:

Returns true if the Runnable was successfully placed in to the message queue. Returns false on failure, usually because the looper processing the message queue is exiting.

我們可以通過呼叫handler的post方法,把Runnable物件(一般是Runnable的子類)傳過去;handler會在looper中呼叫這個Runnable的Run方法執行。

Runnable是一個介面,不是一個執行緒,一般執行緒會實現Runnable。所以如果我們使用匿名內部類是執行在UI主執行緒的,如果我們使用實現這個Runnable介面的執行緒類,則是執行在對應執行緒的。

具體來說,這個函式的工作原理如下:

View.post(Runnable)方法。在post(Runnable action)方法裡,View獲得當前執行緒(即UI執行緒)的Handler,然後將action物件post到Handler裡。在Handler裡,它將傳遞過來的action物件包裝成一個Message(Message的callback為action),然後將其投入UI執行緒的訊息迴圈中。在Handler再次處理該Message時,有一條分支(未解釋的那條)就是為它所設,直接呼叫runnable的run方法。而此時,已經路由到UI執行緒裡,因此,我們可以毫無顧慮的來更新UI。

如下圖,前面看到的程式碼,我們這裡Message的callback為一個Runnable的匿名內部類

這種情況下,由於不是在新的執行緒中使用,所以千萬別做複雜的計算邏輯。

image



第四:在多執行緒程式設計這塊,我們經常要使用Handler,Thread和Runnable這三個類,那麼他們之間的關係你是否弄清楚了呢?

  首先說明Android的CPU分配的最小單元是執行緒,Handler一般是在某個執行緒裡建立的,因而Handler和Thread就是相互繫結的,一一對應。

  而Runnable是一個介面,Thread是Runnable的子類。所以說,他倆都算一個程序。

  HandlerThread顧名思義就是可以處理訊息迴圈的執行緒,他是一個擁有Looper的執行緒,可以處理訊息迴圈。

  與其說Handler和一個執行緒繫結,不如說Handler是和Looper一一對應的。

  最後需要說明的是,在UI執行緒(主執行緒)中:

  mHandler=new Handler();

  mHandler.post(new Runnable(){

  void run(){

  //執行程式碼...

  }

  });

  這個執行緒其實是在UI執行緒之內執行的,並沒有新建執行緒。

  常見的新建執行緒的方法是:

  Thread thread = new Thread();

  thread.start();

  HandlerThread thread = new HandlerThread("string");

  thread.start();

第五: Java Runnable介面在進行相關編寫的時候需要我們不斷的學習相關程式碼。下面我們就來看炫如何才能使用相關的程式碼。Runnable介面只有一個方法run(),我們宣告自己的類實現Runnable接 口並提供這一方法,將我們的執行緒程式碼寫入其中,就完成了這一部分的任務。

  但是Runnable介面並沒有任何對執行緒的支援,我們還必須建立Thread類 的例項,這一點通過Thread類的建構函式public Thread(Runnable target);來實現。下面是一個例子:

  1.public class MyThread implements Runnable

  2.{

  3.int count= 1, number;

  4.public MyThread(int num)

  5.{

  6.numnumber = num;

  7.System.out.println("建立執行緒 " + number);

  8.}

  9.public void run()

  10.{

  11.while(true)

  12.{

  13.System.out.println

  14.("執行緒 " + number + ":計數 " + count);

  15.if(++count== 6) return;

  16.}

  17.}

  18.public static void main(String args[])

  19.{

  20.for(int i = 0; i 〈 5;

  21.i++) new Thread(new MyThread(i+1)).start();

  22.}

  23.}

  嚴格地說,建立Thread子類的例項也是可行的,但是必須注意的是,該子類必須沒有覆蓋 Thread 類的 run 方法,否則該執行緒執行的將是子類的 run 方法,而不是我們用以實現Runnable 介面的類的 run 方法,對此大家不妨試驗一下。

  使用 Java Runnable介面來實現多執行緒使得我們能夠在一個類中包容所有的程式碼,有利於封裝,它的缺點在於,我們只能使用一套程式碼,若想建立多個執行緒並使各個執行緒執行不同的代 碼,則仍必須額外建立類,如果這樣的話,在大多數情況下也許還不如直接用多個類分別繼承 Thread 來得緊湊。

參考:

http://www.cnblogs.com/qingblog/archive/2012/08/08/2628245.html

http://java.chinaitlab.com/net/807900.html

http://czhjchina.blog.163.com/blog/static/20027904720111153382495/

http://zhidao.baidu.com/question/82198667.html

http://developer.51cto.com/art/201203/321042.htm




           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述