線程(Thread)相關概念
進程(Process)
同一個操作系統中執行的子程序,包含虛擬CPU、代碼、數據三部分。進程一般指一個執行單元,在PC和移動設備上指一個程序或一個應用。一個進程可以包含多個線程,進程跟線程可以理解爲包含與被包含的關係。
線程(Thread)
同一進程中執行的子程序,Java語言的概念,實際執行任務的基本單元,Android異步處理技術的基礎。
線程是CPU調度的最小單元,是一種有限的系統資源。通常情況下進程中會存在大量耗時任務,如果這些任務在UI線程中進行會造成異常,所以解決這個問題就需要把耗時任務放到子線程中處理。
多線程
同一進程中併發執行的多個子程序流,可以提高CPU的使用率;
多線程的作用
多線程可以發揮多核CPU的優勢、防止阻塞、便於建模;
Android中常見線程
主線程(UI線程)
主線程又叫做UI線程,隨着應用啓動而啓動,用來運行Android組件,刷新屏幕上的UI元素。只有主線程才能操作UI,因爲Android UI工具包不是線程安全的。
主線程負責的內容包括UI生命週期控制、系統事件處理、消息處理、界面佈局、界面繪製、界面刷新。除此之外,儘量避免將其他處理放在主線程中,特別是複雜的數據計算和網絡請求等。
子線程
子線程也叫工作線程,除了主線程之外都是子線程;
Binder線程
Binder線程用於不同進程之間線程的通信,每個進程都維護了一個線程池用來處理其他進程中線程發送的消息,這些進程包括系統服務、Intents、ContentProvider和Service等;
後臺線程
在應用中顯式創建的線程都是後臺線程。剛創建出來時這些線程的執行是空的,需要手動添加任務;
Linux系統層面,主線程和後臺線程一樣。在Android框架中,通過WindowManager賦予了主線程只能處理UI更新以及後臺線程不能直接操作UI的線程。
實現Thread的方式
實現線程一般我們有兩種方式,第一種是繼承Thread類,第二種是實現Runnable接口。
java.lang.Thread類的實例就是一個線程,它需要調用java.lang.Runnable接口來執行,線程類本身就是調用Runnable接口,所以實現線程既可以繼承java.lang.Thread類也可以實現Runnable接口來重寫run()方法。
實際上Thread也算是一個Runnable,它內部實現了Runnable接口,在Thread類中有一個Runnable類型的target字段,代表要被執行在這個子線程中的任務。Thread只是對Runnable的封裝,並且通過一些狀態對Thread進行管理與調度。
當啓動一個Thread的時候如果Thread的target不爲空,則會在子線程中執行這個target的run()函數,否則虛擬機就會執行該線程自身的run()函數。
兩種創建方式工作時性質是一樣的,建議使用實現Runnable接口方式可以避免單繼承的侷限性。
//繼承Thread類
private class SyncThread extends Thread {
SyncThread(String name) {
super(name);
}
@Override
public void run() {
//執行耗時操作
}
}
//實現Runnable接口
//Runnable接口定義了可執行的任務,只有一個無返回值的run()函數
private class SyncRunnable implements Runnable {
@Override
public void run() {
//執行耗時操作
}
}
線程相關重要方法
wait()
當線程執行到wait()方法的時候它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖,使得其他線程可以訪問。用戶可以使用notify()、notifyAll()或指定睡眠時間來喚醒當前等待池中的線程。
wait()、notify()通常用於等待機制的實現,當條件未滿足的時候調用wait()進入等待狀態,一旦條件滿足調用notify()或notifyAll()喚醒等待的線程繼續執行。
wait()、notify()、notifyAll()必須放在synchronized block中,否則報錯。
join()
阻塞當前調用join()函數所在的線程,直到接收線程執行完畢後再繼續。等待目標線程執行完成之後再繼續執行,目標線程未執行完之前,加入線程無法進行操作。
yield()
線程禮讓,也叫做線程讓步。目標線程由運行狀態轉換爲就緒狀態,讓出執行權限讓其他線程先執行,其他線程能否優先執行是未知的。
yield()與join()類似,爲使調用該方法的線程讓出執行時間給其他已就緒狀態的線程。線程的執行是有時間片的,每個線程輪流佔用CPU固定的時間,執行週期到了之後就讓出執行權給其他的線程。yield()的功能就是主動讓出線程的執行權交由其他線程,其他線程能否得到優先執行就看各個線程的狀態。
sleep(long milis)
在指定毫秒數內讓當前正在執行的線程休眠(暫停執行),本線程不會去搶,除非sleep()結束,多個線程之間都會去搶執行權限,不會考慮優先級;
currentThread()
返回對當前正在執行的線程對象的引用(實現接口方式時使用);
setDaemon(boolean on)
將該線程標記爲守護線程,守護線程需要依賴其他線程,會在虛擬機停止的時候停止。一個線程守護另一個線程,守護的線程稱爲守護線程,被守護的線程稱爲非守護線程,作用是爲其他線程運行提供便利服務。
線程安全
如果代碼在多線程下執行和在單線程下執行永遠都能獲得一樣的結果,代碼就是線程安全的。
爲了保證數據在多線程操作中的一致性我們需要通過同步鎖保證在同一時刻只有一個線程能夠訪問到該對象或數據,對它進行修改後再將最新數據同步,使得其他線程能夠得到這個最新的數據。
Synchronized
Synchronized是一個Java關鍵字,是最常用的同步機制,是一種基於語言的粗略鎖,能夠作用於對象、函數、類。每個對象都只有一個鎖,誰能夠拿到這個鎖誰就能得到訪問權限。
synchronized作用於類時鎖的是這個類,不是具體的對象;
synchronized作用於函數,實際上鎖的也是對象,鎖定的對象就是該函數所在類的對象;
synchronized作用於對象的時候是防止其他線程訪問同一個對象synchronized代碼塊或函數。
停止線程
- 使用退出標誌,使線程正常的執行完run()方法終止;
- 使用interrupt方法使線程異常,線程進行捕獲或拋異常,正常執行完run方法終止;
- 使用stop()方法強制退出;
線程優先級
線程優先級範圍爲1-10,API提供等級分爲:低(MIN_PRIORITY=1),中(NORM_PRIORITY=5),高(MAX_PRIORITY=10)
線程優先級特點
- 繼承特性(線程A中啓動線程B,線程B繼承了A的優先級);
- 隨機性(線程調度的順序不一定是根據優先級,具有隨機性);
public class ThreadPriorityDemo {
public static void main(String[] args) {
Thread thread = new ThreadPriority("thread_1<<<<");
Thread thread_1 = new ThreadPriority(">>>thread_2");
thread_1.setPriority(Thread.MIN_PRIORITY); //<設置線程優先級
thread.setPriority(Thread.MAX_PRIORITY);
thread_1.start();
thread.start();
}
}
class ThreadPriority extends Thread {
public ThreadPriority(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("" + Thread.currentThread().getName() + ",number:" + i + ",Priority:" + Thread.currentThread().getPriority());
}
}
}