Android中提供瞭如下幾種實現多線程的方法: java
1. Thread+Handler實現多線程:
不要在本身新建的Thread裏對UI進行操做。可使用Handler與主線程進行交互。有如下兩種使用方式:
<1>. 發送消息: 數據庫
// Handler定義在哪一個線程,就被綁定在該線程 Handler handler = new Handler() { public void handleMessage(Message msg) { super.handleMessage(msg); // 在handler所在的線程上處理消息,若是handler所在的線程是主線程,能夠更新UI } }; // 新建線程Thread new Thread(new Runnable(){ public void run() { while(!Thread.currentThread().isInterrupted()) { Message msg = handler.obtainMessage(); msg.what = XX; msg.arg1 = XX; msg.obj = XX; msg.sendToTarget(); // 在新線程中分發Message對象到handler所在的線程 } } }).start();
<2>. 發送Runnable對象: 緩存
// Handler定義在哪一個線程,就被綁定在該線程 Handler handler = new Handler(); // 新建線程Thread new Thread(){ public void run(){ while(!Thread.currentThread().isInterrupted()) { handler.post(new Runnable(){ // 在新線程中分發Runnable對象到handler所在的線程中 public void run(){ // 運行在handler所在的線程,若是handler所在的線程是主線程,能夠更新UI } }); } } }.start();// 新建了線程
以上例子都是在主線程中建立Handler,在子線程中引用。若是要在本身的Thread中建立Handler來與主線程交互的話,必須在new Handler前加上Looper.prepare(),在建立後加上Looper.loop()。緣由:只有主線程默認帶了消息隊列,自定義線程是不帶MessageQueue和Looper的。 安全
<3>.Thread中斷問題: 多線程
雖然有stop()接口,可是不安全,會發生異常。因此,經過調用interrupt()設置中斷標示,在Thread的run()函數中,若是有循環語句,在循環中經過斷定isInterrupted(),若是爲true,主動退出run()函數便可結束線程,達到中斷的目的。 併發
<4>. 一組線程的執行是無序的,能夠實現協同工做,也能夠各自獨立工做。 框架
// 多個線程共享同一個TaskRun的實例,即實現協做工做。
TaskRun task = new TaskRun(); //TaskRun實現Runnable接口的run()函數 異步
new Thread(task,"t1").start();
new Thread(task,"t2").start();
// 多個線程各自初始化各自的實例,即各自作各自的工做。
MyThread myT1 = new MyThread(); //MyThread繼承了Thread,本身實現run()函數
MyThread myT2 = new MyThread();
myT1.start();
myT2.start(); ide
2. AsyncTask 異步任務: 函數
一個實例僅執行一次,屢次execute會拋出異常。因此,要執行幾個任務就new幾個實例。內部會建立一個進程做用域的線程池來管理要運行的任務。
2.3平臺之前調用execute,全部的任務併發執行,內部的線程池限制是5個,後來默認串行執行,只能調用executeOnExecutor((ExecutorService)Executors.newCachedThreadPool())實現併發,這裏沒有使用默認的線程池,而使用自定義的。
適用於短期的多線程任務(幾分鐘)。若是要完成長時間的運行任務,最好使用Executor,ThreadPoolExecutor,FutureTask。
<1>.AsyncTask<Params, Progress, Result>
是抽象類,定義了三種泛型類型,分別表明「啓動任務執行的輸入參數」(如HTTP請求的URL)、「後臺任務執行的進度」、「後臺計算結果的類型」。若是沒有被使用,能夠用Void代替。
<2>.AsyncTask定義的方法:
execute(Params... params),執行一個異步任務,須要咱們在代碼中調用此方法,觸發異步任務的執行。
onPreExecute(),在execute(Params... params)被調用後當即執行,通常用來在執行後臺任務前對UI作一些標記。
doInBackground(Params... params),在onPreExecute()完成後當即執行,用於執行較爲費時的操做,此方法將接收輸入參數和返回計算結果。不能更新UI,只能在執行過程當中調用publishProgress(Progress... values)來更新進度信息。
onProgressUpdate(Progress... values),在調用publishProgress(Progress... values)時被執行,將進度信息更新到UI組件上。
onPostExecute(Result result),當後臺操做結束時,此方法將會被調用,計算結果將作爲參數傳遞到此方法中,直接將結果顯示到UI組件上。
<3>.終止線程:
cancel()終止後臺線程的運行,能夠先用isCanceled()來詢問,避免重複叫停。
執行cancel(),將會回調onCanceled(),同時onPostExecute()就不會被回調。若是想作到及時中斷,在doInBackground()中,檢測該狀態位來判斷是否繼續運行。
在 Java 中,非靜態匿名內部類會持有其外部類的隱式引用,該引用會致使 Activity 被保留,而不是被垃圾回收機制回收。將線程類(Thread,AsyncTask)聲明爲私有的靜態內部類避免了 Activity context 的內存泄漏問題,但在配置發生改變後,線程仍然會執行。緣由在於,DVM 虛擬機持有全部運行線程的引用,不管這些線程是否被回收,都與 Activity 的生命週期無關。運行中的線程只會繼續運行,直到Android 系統將整個應用進程殺死。在退出當前Activity 前使用 onDestroy() 方法結束運行中線程是個不錯的選擇。
private MyTask task; private static int i = 1; private static class MyTask extends AsyncTask<Void, Void, Integer>{ @Override protected Integer doInBackground(Void... params) { while(!isCancelled()) { i++; Log.d("i = ", String.valueOf(i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); task = new MyTask(); task.execute(); } @Override protected void onDestroy() { super.onDestroy(); if(task!=null && task.getStatus()==Status.RUNNING) { task.cancel(true); } }
3. 線程池:
Java經過Executors提供的四種線程池,分別爲:
<1>. newCachedThreadPool建立一個可緩存線程池,調用 execute 將重用之前構造的線程(若是線程可用),若無可回收,則新建線程。
有IDLE機制,線程若是超過TIMEOUT(60s)不活動,其會自動被終止。一般用於執行一些生存期很短的異步型任務。
<2>. newFixedThreadPool 建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
沒有IDLE機制,在某個線程被顯式地關閉以前,池中的線程將一直存在。多數針對一些很穩定很固定的正規併發線程。
定長線程池的大小最好根據系統資源進行設置。如Runtime.getRuntime().availableProcessors()
<3>. newScheduledThreadPool 建立一個定長線程池,支持定時及週期性任務執行。
如下是3種定時器的寫法與比較:
Handler.postDelayed其實是運行在主線程的,也能夠新建一個Thread來發送Runnable。
在Timer機制中,只封裝了一個線程來執行定時任務,並且有時有異常發生。
ScheduledExecutorService比Timer更安全,功能更強大,是最優化方案。
private static final int REGUEST_MESSAGE_TIME = 1000*60*60*2;//2小時 //使用Handler做爲定時器,實際上Runnable仍是在主線程中運行 private Handler mTimerHandler = new Handler(); private Runnable mTimerRunnable = new Runnable() { @Override public void run() { // 2小時後執行一次Runnable msgTimerHandler.postDelayed(this, REGUEST_MESSAGE_TIME); } }; // Timer + TimerTask定時器,在Timer機制中,只有一個線程來執行定時任務 private Timer mTimer = null; private TimerTask mTimerTask = new TimerTask() { @Override public void run() { } }; // 使用線程池的方式支持定時線程任務 private ScheduledExecutorService mScheduledExecutorService = null; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 立刻執行Runnable msgTimerHandler.post(mTimerRunnable); mTimer = new Timer(); // 每隔2小時執行一次TimerTask,循環任務 mTimer.schedule(mTimerTask, 0, REGUEST_MESSAGE_TIME); // 該線程池能夠容納5個線程 mScheduledExecutorService= Executors.newScheduledThreadPool(5); // 立刻執行Runnable ,而後每隔2小時執行一次Runnable mScheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { } }, 0, 2, TimeUnit.HOURS); } protected void onDestroy() { super.onDestroy(); // 清除以該Handler爲target的全部Message和Callback,防止內存泄露 msgTimerHandler.removeCallbacksAndMessages(null); if ( mTimer != null ) { //不但能夠結束當前schedule,連整個Timer的線程都會結束掉 mTimer.cancel(); mTimer = null; } //關閉線程池隊列中的全部等待任務 mScheduledExecutorService.shutdown(); }
<4>. newSingleThreadExecutor 建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。 不用考慮同步的問題。
沒有IDLE機制。可用於數據庫操做,文件操做,應用批量安裝,應用批量刪除等不適合併發但可能IO阻塞性及影響UI線程響應的操做。
好處:
a. 重用存在的線程,減小對象建立、消亡的開銷,性能佳。
b. 可有效控制最大併發線程數,提升系統資源的使用率,同時避免過多資源競爭,避免堵塞。
c. 提供定時執行、按期執行、單線程、併發數控制等功能。
開啓線程的兩種方法:
Future<?> submit(Runnable task) /*用Future來判斷線程運行狀態*/
void execute(Runnable command) /*沒法判斷線程是否成功完成*/
關閉線程池的方法:
shutdown() 中止接受新任務且等待已經提交的任務執行完成(已經提交的任務會分兩類:一類是已經在執行的,另外一類是尚未開始執行的),當全部已經提交的任務執行完畢後將會關閉ExecutorService。
shutdownNow() 試圖中止全部正在執行的活動任務(調用對應線程的interrupt(),設置中斷標誌位而已),暫停處理正在等待的任務,並返回等待執行的任務列表。
中斷線程池某個任務:
向ExecutorService提交任務調用submit方法以後,返回值是個Future對象,可使用這個對象的cancel方法來取消對應任務。
若是任務還在等待隊列中,會直接取消掉。
若是任務已經執行了,cancel的boolean值參數mayInterruptIfRunning用於表示當任務已經開始執行時,是否須要嘗試中斷執行該任務的線程。(注意:這只是表示任務是否可以接收中斷,而不是表示任務是否能檢測並處理中斷)
因此仍是得在本身的任務中經過斷定中斷標誌位來結束任務。
建議:
與主線程有交互時用AsyncTask,不然就用Thread。
當有須要大量線程執行任務時,必定要建立線程池,好比批量下載圖片或文件。
對於想要當即開始執行的異步任務,要麼直接使用Thread,要麼單首創建線程池提供給AsyncTask,默認的AsyncTask不必定會當即執行你的任務。
4. 訪問臨界資源時,使用線程同步機制:
synchronized關鍵字能夠修飾方法,也能夠修飾代碼塊,但不能修飾構造器,屬性等。
5. 處理線程阻塞中斷:
在執行涉及線程調度的阻塞調用時(例如wait、sleep和join),若是發生中斷,被阻塞線程會「儘量快的」拋出InterruptedException。所以,咱們就能夠用下面的代碼框架來處理:
try {
//wait、sleep或join
}
catch(InterruptedException e) {
//某些中斷處理工做
}
<1>.sleep()靜態方法
在指定時間內讓當前正在執行的線程暫停執行,但不會釋放「鎖標誌」。
使當前線程進入阻塞狀態,在指定時間內不會執行。其餘線程在此期間能夠得到運行機會。線程睡眠到期自動甦醒,並返回到可運行狀態,不是運行狀態。什麼時候運行還要看線程池如何調度。
<2>.wait()方法
在其餘線程調用對象的notify或notifyAll方法前,致使當前線程等待。線程會釋放掉它所佔有的「鎖標誌」,從而使別的線程有機會搶佔該鎖。
當前線程必須擁有當前對象鎖。若是當前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常。
喚醒當前對象鎖的等待線程使用notify或notifyAll方法,也必須擁有相同的對象鎖,不然也會拋出IllegalMonitorStateException異常。
wait()和notify()必須在synchronized函數或synchronized block中進行調用。若是在non-synchronized函數或non-synchronized block中進行調用,雖然能編譯經過,但在運行時會發生IllegalMonitorStateException的異常。
<3>.yield靜態方法
暫停當前正在執行的線程對象,並執行其餘線程。
yield()只是使當前線程從新回到可執行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行。
yield()只能使同優先級或更高優先級的線程有執行的機會。
<4>.join方法
保證當前線程中止執行,直到該線程所加入的線程完成爲止。然而,若是它加入的線程沒有存活,則當前線程不須要中止。
如在t1線程中調用t2.join(),則須要t2線程執行完後t1方能繼續執行。