Android/java 多執行緒(一)-Thread的使用以及原始碼分析
執行緒的概念以及狀態
在學習執行緒之前,我們需要普及一個概念,每一個程式執行都會有一個父程式,我們的執行緒就是在此父程式中執行,拿Android來說,預設情況下,啟動一個程式,所有的元件程式都執行在同一個程式中,並且會建立一個執行執行緒在該程式中,俗稱"主執行緒",當我們在該執行緒中做了耗時的操作造成了程式卡頓,我們就叫“執行緒阻塞”了,此時就應該另起一個執行緒來執行耗時操作。
要了解執行緒中方法的使用,就得先了解執行緒的執行狀態,執行緒從建立到執行完畢,一共有6個狀態:
NEW(執行緒建立未啟動)
RUNNABLE(正在執行中的執行緒)
BLOCKED(被阻塞並且在等待監視器鎖釋放)
WAITING(等待被喚醒)
TIMED_WAITING(等待或睡眠一定時間被喚醒)
TERMINATED(執行緒終止,消亡)
這些狀態對應Thread
原始碼中的State
列舉,透過getState()
方法可以獲取該執行緒的執行狀態
其中BLOCKED
表示等待監視器鎖的過程,那麼什麼是監視器鎖呢,監視器鎖是為了解決執行緒不安全而誕生的方法,當多個程式同時操作一個資料結構並修改時,這時資料結構是不確定的,我們稱之為“執行緒不安全”,於是我們使用synchronized(同步鎖)
和lock鎖
等機制來解決這種執行緒不安全,套上此鎖,當前有且只有一個執行緒能修改此資料結構,其他的執行緒則需要等待,這就保證了資料結構的一致性,而其他執行緒會進入等待的狀態,也就是我們的BLOCKED
狀態。synchronized
使用示例:
class MyThread extends Thread { @Override public void run() { synchronized (this) { //這裡寫修改資料來源的程式碼 } } }
lock
使用示例:
class MyThread extends Thread { private final ReentrantLock lock = new ReentrantLock (); @Override public void run() { lock.lock(); //這裡寫修改資料來源的程式碼 lock.unlock(); } }
當然這兩者的應用不僅僅是線上程中,在必要的方法呼叫上也可使用
執行緒的原始碼以及方法使用
Thread
原始碼:
public class Thread implements Runnable { ........... /* Some of these are accessed directly by the VM; do not rename them. */ private volatile long nativePeer; volatile ThreadGroup group; volatile boolean daemon; volatile String name; volatile int priority; volatile long stackSize; Runnable target; private static int count = 0; private long threadInitNumber ; /** * Normal thread local values. */ ThreadLocal.Values localValues; /** * Inheritable thread local values. */ ThreadLocal.Values inheritableValues; /** Callbacks to run on interruption. */ private final List<Runnable> interruptActions = new ArrayList<Runnable>(); /** * Holds the class loader for this Thread, in case there is one. */ private ClassLoader contextClassLoader; /** * Holds the handler for uncaught exceptions in this Thread, * in case there is one. */ private UncaughtExceptionHandler uncaughtHandler; /** * Holds the default handler for uncaught exceptions, in case there is one. */ private static UncaughtExceptionHandler defaultUncaughtHandler; ..... }
Thread實現了Runnable介面,並宣告瞭一些執行緒常用的一些變數:
group
執行緒組,執行緒組包含其他的執行緒組,形成了一個樹結構,除了初始的執行緒組外,其他的執行緒組都會有個父程式,其中執行緒能訪問當前執行緒組的資訊,但不能訪問父執行緒組的資訊daemon
是否是守護執行緒,啥是守護執行緒呢,守護執行緒是依賴於建立它的執行緒的一種執行緒,與普通執行緒的區別是當建立它的執行緒關閉了那它也會關閉,而普通執行緒不會,像我們的垃圾收集器執行緒就是一個守護執行緒threadInitNumber 當前執行緒的識別符號,它是按照執行緒的建立順序來疊加的
name 執行緒的名稱,未指定的話就以
"Thread-" + threadInitNumber
的邏輯命名priority
執行緒優先順序,有三個狀態MIN_PRIORITY
,NORM_PRIORITY
,MAX_PRIORITY
,分別對應低,中,高stackSize 堆疊大小
target 當前的目標執行緒
contextClassLoader 類載入器,用於儲存該執行緒的資訊,斷點續傳等功能可以用到
uncaughtHandler
執行緒未捕獲異常呼叫類,如果沒有對其進行設定,將會預設使用defaultUncaughtHandler
來處理異常,由於我們的主執行緒也是一個Thread,所以我們可以透過實現UncaughtExceptionHandler
介面並呼叫Thread.setDefaultUncaughtExceptionHandler()
方法將我們自定義的異常捕獲類設定給Thread,這樣我們就可以捕獲全域性的異常
瞭解了一些常用變數後,我們再來看一下其中的一些常用方法:
start()方法
用於啟動一個執行緒,只有呼叫此方法,系統才會新開啟一個執行緒並分配給其必要的資源。我們看一下Thread的構造方法,發現最終都是呼叫了init
方法,裡面也只是對基本變數進行初始化,並沒有分配到任何的資源:
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { Thread parent = currentThread(); if (g == null) { g = parent.getThreadGroup(); } g.addUnstarted(); this.group = g; this.target = target; this.priority = parent.getPriority(); this.daemon = parent.isDaemon(); setName(name); init2(parent); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; tid = nextThreadID(); }
所以,新建一個執行緒不會消耗資源,只有當star後才會去分配必要的資源,此時執行緒狀態由NEW
變為了RUNNABLE
run()方法
此方法用於處理執行緒中執行的邏輯,如果繼承Thread類則必須重寫該方法
sleep()方法
sleep(long millis) //引數為毫秒sleep(long millis,int nanoseconds) //第一引數為毫秒,第二個引數為納秒
讓執行緒休眠一段時間,相當於讓執行緒進入阻塞狀態。注意即使執行緒休眠了其鎖機制依舊生效,其他的執行緒依舊不能訪問其被鎖的資料結構,直到其釋放物件鎖,此時執行緒狀態由RUNNABLE
進入TIMED_WAITING
yield()方法
讓當前執行緒交出相應的許可權,為其他執行緒讓步,這是防止CPU過度使用的一種有效手段。效果與sleep
差不多,但不能指定具體的時間,並且並不是讓執行緒進入到阻塞狀態,而是進入就緒狀態.此時執行緒狀態由RUNNABLE
進入WAITING
join()方法
join() join(long millis) //引數為毫秒join(long millis,int nanoseconds) //第一引數為毫秒,第二個引數為納秒
此方法是讓執行緒優先來執行,其餘的執行緒會暫停,直到此執行緒執行完畢才會繼續執行。你可以將執行緒看成一些正常行駛的車輛(線上程佇列中),當給其中一輛車使用了join(彎道超車),那其餘的車會給其讓行(執行緒進入暫停狀態),優先讓它行駛(join的執行緒執行),直到它到達終點(執行完成),才會繼續行駛(繼續執行)
而join
原始碼中是呼叫的wait()
方法,而wait()
方法能使執行緒進入阻塞狀態並釋放物件鎖,所以join
方法也是能釋放物件鎖的
interrupt()方法
中斷執行緒,我們知道,執行緒執行完run方法裡的邏輯就會進入終止狀態。呼叫此方法會使一個阻塞的執行緒丟擲InterruptedException
異常,從而終止執行緒,可以呼叫isInterrupted()
判斷是否終止了執行緒,但非阻塞執行緒如何中斷呢,請看:
class MyThread extends Thread{ private volatile boolean isStop = false; @Override public void run() { int i = 0; while(!isStop){ i++; } } public void setStop(boolean stop){ this.isStop = stop; } }
我們可以設定一個變數來控制是否執行完畢,從而結束執行緒,此時執行緒狀態進入TERMINATED
狀態
作者:我是黃教主啊
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3402/viewspace-2821569/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java多執行緒之Thread原始碼分析Java執行緒thread原始碼
- Java多執行緒Thread類使用Java執行緒thread
- Android/java 多執行緒(二)-Thread的好兄弟HandlAndroidJava執行緒thread
- java多執行緒之Thread類Java執行緒thread
- Java多執行緒(二):Thread類Java執行緒thread
- Java多執行緒類FutureTask原始碼閱讀以及淺析Java執行緒原始碼
- java 多執行緒(關於Thread的講解)Java執行緒thread
- Java 中的執行緒 threadJava執行緒thread
- Java多執行緒(04)執行緒狀態與上線文切換以及Thread方法介紹Java執行緒thread
- ConcurrentHashMap執行緒安全機制以及原始碼分析HashMap執行緒原始碼
- Java多執行緒-執行緒池的使用Java執行緒
- Java之實現多執行緒的方式一 :繼承Thread類Java執行緒繼承thread
- java多執行緒之執行緒的基本使用Java執行緒
- Swift多執行緒:使用Thread進行多執行緒間通訊,協調子執行緒任務Swift執行緒thread
- Java實現多執行緒詳解一 ( 繼承Thread方式 )Java執行緒繼承thread
- @Java | Thread & synchronized – [ 執行緒同步鎖 基本使用]Javathreadsynchronized執行緒
- 多執行緒基礎(十九):Semaphore原始碼分析執行緒原始碼
- java使用多執行緒Java執行緒
- 執行緒以及多執行緒,多程式的選擇執行緒
- java執行緒池原始碼一窺Java執行緒原始碼
- Java 多執行緒(Java.Thread)------ 執行緒協作(生產者消費者模式)Java執行緒thread模式
- Java多執行緒學習(一)Java多執行緒入門Java執行緒
- Java多執行緒的使用(二)Java執行緒
- java多執行緒核心api以及相關概念(一)Java執行緒API
- Java多執行緒(一)多執行緒入門篇Java執行緒
- Java排程執行緒池ScheduledThreadPoolExecutor原始碼分析Java執行緒thread原始碼
- 比特幣原始碼分析:多執行緒檢查指令碼比特幣原始碼執行緒指令碼
- 執行緒池原始碼分析執行緒原始碼
- 執行緒和程式基礎以及多執行緒的基本使用(iOS)執行緒iOS
- 詳解Java執行緒池的ctl(執行緒池控制狀態)【原始碼分析】Java執行緒原始碼
- 一日一技 (2) Java執行緒Thread怎麼使用?Java執行緒thread
- Java多執行緒下載分析Java執行緒
- Android多執行緒之Handler、Looper與MessageQueue原始碼解析Android執行緒OOP原始碼
- Java多執行緒-程式執行堆疊分析Java執行緒
- Thread(執行緒)thread執行緒
- 執行緒池的建立和使用,執行緒池原始碼初探(篇一)執行緒原始碼
- 多執行緒系列(二)之Thread類執行緒thread
- Android多執行緒之執行緒池Android執行緒