線程基礎知識
線程狀态
線程可以有如下6中狀态
- New(新建立)
- Runable(可運作)
- Blocked(被阻塞)
- Waiting(等待)
- Timed waiting(計時等待)
-
Terminated(被終止)
可調用用getState()方法擷取目前狀态
新建立線程
比如new Thread(r),該線程還沒有開始運作,意味着它的狀态為new。
可運作線程
一旦調用start方法,線程處于runable狀态,一個可運作的線程可能正在運作也可能沒有運作,這取決與作業系統給線程提供運作的時間。一旦一個線程開始運作,它不必始終都在運作,事實上,運作過程中,它可能會被中斷,而中斷的目的就是讓其他線程獲得運作機會,線程排程的細節依賴于作業系統提供的服務。搶占式排程系統給每一個可運作線程一個時間片來執行任務,當時間片用完,作業系統剝奪該線程的運作權,并給另一個線程運作機會。
被阻塞線程和等待線程
當線程被阻塞或等待線程時,它暫時不活動。它不運作任何代碼且消耗最少的資源,知道線程排程器激活它。細節取決于它是怎樣達到非活動狀态的。
- 當一個線程試圖擷取一個内部的對象鎖,而該鎖被其它線程所有,則該線程進入阻塞狀态,當其它線程釋放該鎖,并且線程排程器允許本線程持有它的時候,該線程将變成非阻塞狀态。
- 當線程等待另一個線程通知排程器一個條件時,它自己進入等待狀态。
- 調用Object.wait方法或Thread.join方法
- 等待java.util.concurrent庫中的Lock或Condition時
- 有幾個方法有一個逾時參數。調用它們導緻線程進入計時等待狀态。這一狀态将一直保持到逾時期滿或者接收到适當的通知。帶有逾時期參數的方法有Thread.sleep和Object.wait,Thread.join,Lock.tryLock以及Condition.await的計時版。
被終止的線程
線程因如下兩個原因之一而被終止:
- 因為run方法正常退出而死亡
- 因為一個沒有捕獲的異常終止了run方法而意外死亡
可以調用線程的stop方法殺死線程,該方法抛出一個ThreadDeath錯誤對象,由此殺死對象,但是,stop方法已經過時,不要再自己的代碼中調用這個方法。
線程屬性
包括:線程優先級,守護線程,線程組以及處理未捕獲異常的處理器
線程優先級
每一個線程有一個優先級。預設情況下,一個線程繼承它的父線程的優先級。可以用setPriority方法提高或降低任何一個線程的優先級,範圍是MIN_PRIORITY(1)與MAX_PRIORITY(10)之間的任何值。NORM_PRIORITY被定義為5.
每當線程排程器有機會選擇新線程時,它首先會選擇具有較高優先級的線程。
守護線程
可以通過t.setDaemon(true)将線程轉換為守護線程,守護線程的唯一用途是為其它線程提供服務。計時線程就是一個例子,它定時地發送“計時器滴答”信号給其它線程或清空過時的高速緩存項的線程。當隻剩下守護線程時,虛拟機就退出了,由于如果隻剩下守護線程,就沒必要繼續運作程式了。
未捕獲異常處理器
線程的run方法不能抛出任何被檢測的異常,但是,不被檢測的異常會導緻線程終止,在這種情況下,線程就死亡了。
不需要任何catch字句就可以來處理可被傳播的異常,相反,就線上程死亡之前,異常被傳遞到一個用于未捕獲異常的處理器,該處理器必須屬于一個實作Thread.UncaughtExceptionHandler接口的類,這個接口隻要一個方法:void uncaughtException(Thread t,Throwable e),可以用setUncaughtExceptionHandler為任何線程安裝一個處理器,也可以用Thread類的setDefaultUncaughtExceptionHandler()為所有線程安裝一個預設的處理器。
如果不安裝預設的處理器,預設的處理器為空
同步
在大多數多線程應用中,兩個或兩個以上的線程需要共享對同一資料的存取。根據各線程通路資料的次序,可能會産生訛誤的對象,這種情況成為競争條件。如果并發的每個線程對共享資料的操作源于塊不是原子操作,即它實際上編譯後是多個虛拟機指令,比如說增值指令是由幾條指令組成的,那麼執行它們的線程可以在任何一條指令點上被中斷。
鎖對象
有兩種機制防止代碼塊并發通路的幹擾。
- Java語言提供一個synchronized關鍵字,并且Java SE 5.0引入了ReentrantLock類。synchronized關鍵字自動提供一個鎖以及相關的“條件”,對于大多數需要顯示鎖的情況很便利。
- java.util.concurrent架構
myLock.lock();//a ReentrantLock object
try{
critical section
}
finally{
myLock.unlock();//make sure the lock is unlocked even if an exception is thrown
}
這一結構確定任何時刻隻有一個線程進入臨界區,一旦一個線程封鎖了對象,其它任何線程都無法通過lock語言。當其它線程調用lock時,他們被阻塞,知道第一個線程釋放鎖對象。
android 的消息機制
android的消息機制主要是指handler的運作機制,handler的運作需要底層的MessageQueue和Looper的支撐。MessageQueue是消息隊列,内部儲存了一組消息,以隊列的形式對外提供插入和删除的工作,MessageQueue隻是個消息的存儲單元,它不能去處理消息,而Looper就可以以無限循環的形式去查找是否有新消息,如果有的話就處理,沒有的話就一直等待着。Handler建立的時候會采用目前線程的Looper來構造消息循環系統。
具體介紹,這兒有一篇很好的文章,
http://www.jianshu.com/p/d88e1af5100f
什麼姿勢開線程?
new thread()
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
優點:這種方式簡單粗暴,是Android系統中開線程最簡單的方式,也隻能應用于最簡單的場景,簡單卻伴随着不少的隐患。
缺點:
- 僅僅是啟動了一個新的線程,沒有任務的概念,不能做狀态的管理
- Runnable作為匿名内部類還持有了外部類的引用,先線程退出之前,改引用會一直存在,阻礙外部類對象被GC回收,在一段時間内造成記憶體洩漏。
- 沒有線程切換的接口,要傳遞處理結果到UI線程的話,需要些額外的線程切換代碼。
ThreadPoolExecutor 線程池
優點:
- 重用線程池中的線程, 避免因為線程的建立和銷毀所帶來的性能開銷.
- 有效控制線程池中的最大并發數,避免大量線程之間因為互相搶占系統資源而導緻的阻塞現象.
- 能夠對線程進行簡單的管理,可提供定時執行和按照指定時間間隔循環執行等功能.
AsyncTask
public class MyAsyncTask extends AsyncTask {
@Override
protected Object doInBackground(Object[] params) {
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
}
}
- 幾處回調裡可以有機會去中斷任務,在任務狀态的管理上較之Thread()方式更為靈活,AsyncTask的cancel()方法并不會終止任務的執行,開發者需要自己去檢查cancel的狀态值來決定是否中止任務。
- 線程優先級為background,對UI線程的執行影響極小。
- AsyncTask也有隐式的持有外部類對象引用的問題,需要特别注意防止出現意外的記憶體洩漏。
- AsyncTask由于在不同的系統版本上串行與并行的執行行為不一緻,被不少開發者所诟病,這确實是硬傷,絕大部分的多線程場景都需要明确任務是串行還是并行。
HandlerThread
HandlerThread将Handler,Thread,Looper,MessageQueue幾個概念相結合。Handler是線程對外的接口,所有新的message或者runnable都通過handler post到工作線程。Looper在MessageQueue取到新的任務就切換到工作線程去執行。不同的post方法可以讓我們對任務做精細的控制,什麼時候執行,執行的順序都可以控制。HandlerThread最大的優勢在于引入MessageQueue概念,可以進行多任務隊列管理。
特點:
- HandlerThread背後隻有一個線程,任務是串行執行的。
- HandlerThread産生的線程會一直存活,Looper會在該線程中持續的檢查MessageQueue。
- 較之Thread,AsyncTask需要寫更多的代碼,但在實用性,靈活性,安全性上都有更好的表現。
IntentService
它相當于是Service和HandlerThread的結合體,就是在Service裡面開了個線程,隻是在所有的Message處理完畢之後,工作線程會自動結束,然後Service也會銷毀。
Rxjava
觀察者與被觀察者模式,兩個角色可以在不同的線程執行。