天天看點

java多線程解說【叁】_Thread的常用API實作

上篇文章:java多線程解說【貳】_java記憶體模型

在上文中我們分析了java記憶體模型和volatile、synchronized的實作原理。在這篇文章中,我們繼續分析一下Thread的常用api有哪些,以及它們是如何實作的。

Thread的結構

我們先通過源碼看看Thread中主要封裝了哪些對象:

private char        name[];    //線程的名稱
private int         priority;    //線程的優先級
private boolean     daemon = false;    //該線程是否為守護線程,預設為false
private Runnable target;    //該線程要執行的任務
private ThreadGroup group;    //該線程歸屬的線程組
private ClassLoader contextClassLoader;    //加載器,預設為AppClassLoader
private static int threadInitNumber;    //數字辨別匿名線程
ThreadLocal.ThreadLocalMap threadLocals = null;    //線程私有變量
private long stackSize;    //配置設定給線程的棧空間大小,預設0(不限制)
private long tid;    //線程id
private static long threadSeqNumber;    //線程式列
private volatile int threadStatus = 0;    //線程運作狀态,預設是0(NEW)

volatile Object parkBlocker;    //java.util.concurrent.locks.LockSupport.park使用
private volatile Interruptible blocker;    //枚舉字段,interrupt方法使用
private final Object blockerLock = new Object();    //更改blocker值時使用,作為鎖的對象
           

Thread的構造方法

其完整的構造方法聲明如下:

public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
           

其中,ThreadGroup如果不指定,則為目前線程(即啟動該線程的線程)的ThreadGroup;Runnable就是該線程将要執行的類,分兩種情況,一種是繼承了Thread類,一種是實作了Runnable接口;name就是線程的名稱,如果不指定則為"Thread-索引号(索引從0開始)";stackSize是JVM配置設定給線程的棧空間大小,預設0(不限制)。

Thread關于自身資訊的API

setName(String name)/getName()

讀寫線程名稱

getThreadGroup()

讀線程組

activeCount()

擷取目前線程所線上程組上的存活線程

setDaemon(boolean on)

設定目前線程為守護線程。守護線程不應執行重要的任務,一般用于記錄日志或監控之用,因為jvm當隻有守護線程在運作時就停止

isDaemon()

判斷該線程是否是守護線程

checkAccess()

判斷目前線程是否有權限去操作該線程

holdsLock(Object obj)

判斷該線程是否持有某對象的鎖

getId()

擷取目前jvm中該線程的唯一标示

isAlive()

判斷該線程是否存活,标準是線程已啟動但還沒有執行完畢

getState()

擷取該線程狀态,傳回值為枚舉,包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED

Thread關于執行的API

start()

正确的啟動一個線程的方法。先完成一些初始化動作,然後會通知JVM調用線程的run()方法

run()

隻是單純地執行線程代碼,并沒有啟動一個新的線程。

join(long millis, int nanos)

保證指定時間以内,在JVM中隻有該線程在運作。millis是等待的毫秒數,nanos是微秒數(參數可省)。如果millis填0,則一直等到目前線程執行完畢,JVM才會排程其他線程執行。

exit()

讓該線程運作結束之前做下自身資源清理工作

yield()

當該線程讓出CPU,給其他線程一個擷取排程的機會,但是不確定一定會排程到其他線程上。

sleep(long millis, int nanos)

讓該線程在指定時間以内休眠,需要注意的是在休眠過程中該線程不會釋放持有的鎖。millis是等待的毫秒數,nanos是微秒數(參數可省)。

interrupt()

它不會中斷一個正在運作的線程,實際上是線上程受到阻塞時抛出一個中斷信号,這樣線程就得以退出阻塞的狀态。更确切的說,如果線程被wait、join或sleep阻塞時,那麼它将接收到一個中斷異常(InterruptedException),我們可以在異常塊裡調用本方法,進而提早地終結被阻塞狀态。如果線程沒有被阻塞,這時調用interrupt()将不起作用,僅僅是設定中斷标志位為true的情況

interrupted()

判斷該線程中斷辨別是否為true,傳回該辨別并将辨別置為false

isInterrupted()

判斷該線程中斷辨別是否為true,傳回該辨別。其實該方法和interrupted()方法底層調用的都是isInterrupted(boolean isClearState),隻不過參數不同而已

Thread API中不鼓勵使用的方法

setPriority(int newPriority)/getPriority()

讀寫線程的優先級。關于優先級有三個枚舉值:

MIN_PRIORITY = 1;

NORM_PRIORITY = 5;

MAX_PRIORITY = 10;

資料越大,優先級越高。但是這個方法不推薦使用,是因為并不是所有的JVM都會嚴格按照該優先級的設定而排程線程,而且在有的JVM中,當一個線程的優先級設定過高會導緻隻運作該線程,在該線程運作完之前不會排程到其他線程上

suspend()/resume()

暫停/喚醒該線程。也不推薦使用,因為暫停并不釋放鎖,可能導緻死鎖;而且暫停的線程狀态仍然是RUNNABLE,排查問題很難

destroy()

銷毀該線程,但沒有進行清理工作。線程持有的鎖(monitor)不會釋放

stop(Throwable obj)

停止線程,但沒有進行清理工作。線程持有的鎖(monitor)不會釋放

下篇文章:java多線程解說【肆】_鎖實作:wait()/notify()