Android 多線程編程初探

來源:互聯網
上載者:User

標籤:有一個   簡單   匿名   最新   調度   ide   cli   靜態   使用   

  Android 中的多線程其實就是 Java SE 中的多線程,只是為了方便使用,android 封裝了一些類,如 AsyncTask、HandlerThread 等,在日常的開發過程中,我們往往需要去執行一些耗時的操作,例如發起網路請求,考慮到網速等其他外在的因素,伺服器可能不會立刻響應我們的請求,如果不將這條操作放到子線程中去執行,就會造成主線程被阻塞,今天我們就從多線程的基礎來一起探討

 

一、線程的基本用法

 

對於 Andorid 多線程來說我們最新接觸到的就是 Thread 和 Runnable 通常我們如下來啟動一個新的線程

 

1)繼承自 Thread 來建立子線程

 

定義一個線程只需要建立一個類繼承自 Thread,然後重寫父類的 run 方法即可

 

[java] view plain copy 
  1. /** 
  2.  * 繼承 Thread 建立子線程 
  3.  */  
  4. class MyThread extends Thread {  
  5.     @Override  
  6.     public void run() {  
  7.         //處理具體的邏輯  
  8.     }  
  9. }  

那麼如何啟動這個線程呢?只需要 new 出 MyThread 的執行個體,然後調用它的 start() 方法,這樣 run() 方法中的代碼就會運行在子線程,如下:

 

 

[java] view plain copy 
  1. new MyThread().start();  
 

2)實現 Runnable 介面來建立子線程

 

繼承方式耦合性高,更多時候我們會選擇實現 Runnable 介面的方式來實現一個子線程,如下:

 

[java] view plain copy 
  1. /** 
  2.  * 實現 Runnable 介面建立子線程 
  3.  */  
  4. class MyThread implements Runnable {  
  5.     @Override  
  6.     public void run() {  
  7.         //處理具體的邏輯  
  8.     }  
  9. }  

如果使用這種寫法,啟動線程的方法也需要相應的改變,如下:

 

 

[java] view plain copy 
  1. MyThread myThread = new MyThread();  
  2. new Thread(myThread).start();  

Thread 建構函式接受一個 Runnable 參數,我們 new 出的 MyThread 正是一個實現了 Runnable 介面的對象,所以可以直接將它傳入到 Thread 的建構函式裡,接著就和上面的一樣了,調用 Thread 的 start() 方法,run() 方法中的代碼就會在子線程當中運行了

 

 

3)直接使用匿名類來建立子線程

 

 

[java] view plain copy 
  1. new Thread(new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         //處理具體的邏輯  
  5.     }  
  6. }).start();  

如果你不想再定義一個類去實現 Runnable 介面,也可以使用如上匿名類的方式來實現,這種實現方式也是我們最常用到的

 

以上這些就是我們來建立子線程時使用的不方式,基本和 Java 中一樣

 

4)Thread 和 Runnable 的區別

 

       實際上 Thread 也是一個 Runnable,它實現了 Runnable 介面,在Thread類中有一個 Runnable 類型的 target欄位,代表要被執行在這個子線程中的任務,代碼如下:

Thread 部分源碼:

 

[java] view plain copy 
  1. public class Thread implements Runnable {  
  2.     /* What will be run. */  
  3.     private Runnable target;  
  4.     /* The group of this thread */  
  5.     private ThreadGroup group;  
  6.   
  7.     private String name;  
  8.     /* 
  9.    * The requested stack size for this thread, or 0 if the creator did 
  10.    * not specify a stack size.  It is up to the VM to do whatever it 
  11.    * likes with this number; some VMs will ignore it. 
  12.    */  
  13.     private long stackSize;  
  14.   
  15.     /** 
  16.      * 初始化 Thread 並且將Thread 添加到 ThreadGroup 中 
  17.      * 
  18.      * @param g 
  19.      * @param target 
  20.      * @param name 
  21.      * @param stackSize 
  22.      */  
  23.     private void init(ThreadGroup g, Runnable target, String name, long stackSize) {  
  24.         java.lang.Thread parent = currentThread();  
  25.         //group 參數為空白,則擷取當前線程的才線程組  
  26.         if (g == null) {  
  27.             g = parent.getThreadGroup();  
  28.         }  
  29.         this.group = g;  
  30.         this.name = name;  
  31.         //設定 target  
  32.         this.target = target;  
  33.         /* Stash the specified stack size in case the VM cares */  
  34.         this.stackSize = stackSize;  
  35.     }  
  36.   
  37.     public Thread() {  
  38.         init(null, null, null, 0);  
  39.     }  
  40.   
  41.     public Thread(Runnable target) {  
  42.         init(null, target, null, 0);  
  43.     }  
  44.   
  45.     public Thread(ThreadGroup group, Runnable target) {  
  46.         init(group, target, null, 0);  
  47.     }  
  48.   
  49.     public Thread(Runnable target, String name) {  
  50.         init(null, target, name, 0);  
  51.     }  
  52.   
  53.     public Thread(ThreadGroup group, Runnable target, String name) {  
  54.         init(group, target, name, 0);  
  55.     }  
  56.   
  57.     public Thread(ThreadGroup group, Runnable target, String name,  
  58.                   long stackSize) {  
  59.         init(group, target, name, stackSize);  
  60.     }  
  61.   
  62.     /** 
  63.      * 啟動一個新的線程,如果target 不為空白則執行 target 的 run 函數 
  64.      * 否者執行當前對象的run()方法 
  65.      */  
  66.     public synchronized void start() {  
  67.         //調用 nativeCreate 啟動新線程  
  68.         nativeCreate(this, stackSize, daemon);  
  69.         started = true;  
  70.     }  
  71.   
  72.     @Override  
  73.     public void run() {  
  74.         if (target != null) {  
  75.             target.run();  
  76.         }  
  77.     }  
  78. }  

      上面是 Thread 的部分源碼,我們看到其實 Thread 也實現了 Runnable 介面,最終被線程執行的是 Runnable,而非 Thread,Thread 只是對 Runnable 的封裝,並且通過一些狀態對 Thread 進行管理與調度,Runnable 定義了可執行檔任務它只有一個無傳回值的 run() 函數,如下:

 

 

[java] view plain copy 
  1. public interface Runnable {  
  2.     /** 
  3.      * When an object implementing interface <code>Runnable</code> is used 
  4.      * to create a thread, starting the thread causes the object‘s 
  5.      * <code>run</code> method to be called in that separately executing 
  6.      * thread. 
  7.      * <p> 
  8.      * The general contract of the method <code>run</code> is that it may 
  9.      * take any action whatsoever. 
  10.      * 
  11.      * @see java.lang.Thread#run() 
  12.      */  
  13.     public abstract void run();  
  14. }  

當啟動一個線程時,如果 Thread 的 Target 不為空白時,則會在子線程中執行這個 target 的 run() 函數,否則虛擬機器就會執行該線程自身的 run() 函數

 

 

二、線程的 wait、sleep、join、yield

 

       Thread 的基本用法相對來說比較簡單,通常就是複寫 run() 函數,然後調用線程的 start() 方法啟動線程,接下來我們來看看 wait、sleep、join、yield 的區別

1)wait

       當一個線程執行到 wait() 方法時,它就進入到一個和該對象相關的等待池中,同時釋放對象的機鎖,使得其它線程可以訪問,使用者可以使用 notify、nitifyAll 或者指定睡眠時間來喚醒當前等待池中的線程,注意:wait()、notify()、notifyAll() 必須放在 Synchronized 塊中,否則會拋出異常

2)sleep

       該函數是 Thread 的靜態函數,作用是使調用線程進入睡眠狀態,因為 sleep() 是 Thread 類的靜態方法,因此它不能改變對象鎖,所以當在一個 Synchronized 塊中調用 sleep() 方法時,線程雖然休眠了,但是對象的鎖並沒有被釋放,其它線程無法訪問這個對象,即使睡眠也持有對象的鎖

3)join

等待目標線程執行完之後再繼續執行

4)yield

線程禮讓,目標線程由運行狀態轉換為就緒狀態,也就是讓出執行許可權,讓其它線程可以優先執行,但其他線程能否優先執行未知

 

三、線程池

 

       當我們需要頻繁的建立多個線程進行耗時操作時,每次都經過 new Thread 實現並不是一種好的方式,每次 new Thread 建立和銷毀對象的效能較差,線程缺乏統一管理,可能無限制建立線程,相互之間競爭,可能佔用過多系統資源導致死結,並且缺乏定時執行,定期執行,線程中斷等功能,Java 提供了 4 種線程池,它能夠有效管理,調度線程,避免過多的浪費系統資源,它的優點如下:

 

1)重用存在的線程,減少對象建立,銷毀的開銷

2)可有效控制最大並發線程數,提高系統資源的使用率,同時避免過多的資源競爭,避免堵塞

3)提供定時執行、定期執行、單線程、並發數控制等功能

 

       簡單來說,線程池原理簡單的解釋就是會建立多個線程並進行管理,提交給線程的任務會被線程池指派給其中的線程進行執行,通過線程池的統一調度、管理使得多線程使用更加簡單、高效,如:

       線程池都實現了 ExecutorService 介面,改介面定義了線程池需要實現的介面,它的實現有  ThreadPoolExecutor 和 ScheduledThreadPoolExecutor,ThreadPoolExecutor 也就是我們運用最多的線程池實現,ScheduledThreadPoolExecutor 用於周期性執行任務,通常我們都不會直接通過 new 的形式來建立線程池,由於建立參數過程相對複雜一些,因此 JDK 給我們提供了一個 Executor 工廠類來簡化這個過程

 

線程池的使用準則:

1)不要對那些同步等待的其他任務結果的任務排隊,這可能會導致死結,在死結中所有所有線程都被一些任務所佔用,這些任務依次等待排隊任務的結果,而這些任務又無法執行,因為所有的線程處於忙碌狀態

2)理解任務,要有效調整線程池的大小,你需要理解正在排隊的任務以及它們正在做什麼

3)調整線程池的大小基本上就是避免兩類錯誤,線程太少或線程太多,幸運的是對於大多數應用程式來說,太多和太少之間的餘地相當寬

 

今天介紹的基本都是一些理論的東西,有的看起來可能沒勁,大家就當瞭解一下吧,又到周五了,祝大家周末愉快

 

參考:郭神第一行代碼,Android 進階

Android 多線程編程初探

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.