在Java中,一般創(chuàng)建線程的兩種方式是:繼承Thread類和實(shí)現(xiàn)Runnable接口倔叼。本篇將介紹一下Thread類中相關(guān)的API的作用以及用法周伦。
1.currentThread方法
public static native Thread currentThread();
currentThread()方法返回的是對當(dāng)前正在執(zhí)行線程對象的引用
2. sleep方法
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException
sleep()方法會使當(dāng)前線程進(jìn)入指定毫秒數(shù)的休眠,暫停執(zhí)行投慈,雖然給定了一個休眠的時間,但是最終要以系統(tǒng)的定時器和調(diào)度器的精度為準(zhǔn)冠骄,在休眠期間伪煤,該線程不會放棄monitor對象鎖的所有權(quán),這一點(diǎn)不同于wait()方法凛辣。sleep的用法:Thread.sleep(1000L)
在JDK1.5之后抱既,引入了一個枚舉類TimeUnit,其對sleep進(jìn)行了封裝扁誓,使用該類可以省去時間單位換算的步驟防泵,比如想實(shí)現(xiàn)對線程休眠3小時30分30秒66毫秒,使用如下方式即可:
TimeUnit.HOURS.sleep(3);//小時
TimeUnit.MINUTES.sleep(30);// 分鐘
TimeUnit.SECONDS.sleep(30);// 秒
TimeUnit.MILLISECONDS.sleep(66);// 毫秒
TimeUnit對sleep分裝的方法如下:
public void sleep(long timeout) throws InterruptedException {
if (timeout > 0) {
long ms = toMillis(timeout);
int ns = excessNanos(timeout, ms);
Thread.sleep(ms, ns);
}
}
3. yield方法
public static native void yield();
yield方法暫停當(dāng)前執(zhí)行的線程對象跋理,并執(zhí)行其他線程择克。這個暫停是會放棄CPU資源的,并且放棄CPU的時間不確定前普,有可能剛放棄肚邢,就獲得CPU資源了,也有可能放棄好一會兒拭卿,才會被CPU執(zhí)行骡湖,調(diào)用yield方法會使當(dāng)前線程從RUNNING狀態(tài)切換到RUNNABLE狀態(tài)
4. 線程interrupt方法
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
調(diào)用如下方法會使得當(dāng)前線程進(jìn)入阻塞狀態(tài),而調(diào)用當(dāng)前線程的interrupt方法峻厚,即响蕴,上面的interrupt方法就可以打斷阻塞,
使線程阻塞的方法:
Object的wait方法以及重載的的方法
Thread的sleep方法以及對應(yīng)的重載方法
Thread的join方法以及對應(yīng)的重載方法
=========================================
// 這是一個靜態(tài)方法惠桃,但和isInterrupted()方法有很大區(qū)別浦夷,該方法會直接擦除線程的interrupt標(biāo)識
// 需要注意的是辖试,如果當(dāng)前線程被打斷了,那么第一次調(diào)用interrupted方法會返回true劈狐,并且立即擦除interrupt標(biāo)識
// 第二次以及之后的調(diào)用返回都是false罐孝,除非在此期間線程再一次被打斷
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
=========================================
//主要判斷當(dāng)前線程是否被中斷,該方法僅僅是對interrupt標(biāo)識的一個判斷肥缔,并不會影響標(biāo)識發(fā)生任何該表
public boolean isInterrupted() {
return isInterrupted(false);
}
=========================================
// 本地方法 interrupted方法和isInterrupted方法底層都是調(diào)用的這個方法
private native boolean isInterrupted(boolean ClearInterrupted);
5. 獲取線程ID
public long getId() {
return tid;
}
在一個Java應(yīng)用中莲兢,有一個long型的全局唯一的線程ID生成器threadSeqNumber,每new出來一個線程都會把這個自增一次续膳,并賦予線程的tid屬性改艇,這個是Thread自己做的。如下坟岔,在線程初始化的方法中進(jìn)行設(shè)置:
/* Set thread ID */
tid = nextThreadID();// Thread 的init方法
// 生成線程ID的方法
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
6. 設(shè)置和獲取優(yōu)先級
// 獲取線程優(yōu)先級
public final int getPriority() {
return priority;
}
// 設(shè)置線程的優(yōu)先級
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
一般情況下谒兄,我們是不會用到線程優(yōu)先級的,或者說社付,我們一直在使用默認(rèn)的線程優(yōu)先級5舵变,而優(yōu)先級的范圍是1~10。越大代表優(yōu)先級越高瘦穆。優(yōu)先級高的線程得到的CPU資源比較多,設(shè)置優(yōu)先級有助于幫"線程規(guī)劃器"確定下一次選擇哪一個線程優(yōu)先執(zhí)行赊豌。換句話說扛或,兩個在等待CPU的線程,優(yōu)先級高的線程越容易被CU選擇執(zhí)行碘饼,但這并不意味著優(yōu)先級低的線程就會等待優(yōu)先級高的線程先執(zhí)行完再執(zhí)行熙兔。所以,不要在程序設(shè)計(jì)中企圖使用線程優(yōu)先級綁定某些特定的業(yè)務(wù)艾恼,或者讓業(yè)務(wù)嚴(yán)重依賴線程的優(yōu)先級住涉,這可能達(dá)不到你的預(yù)期。
7. 設(shè)置線程上下文類加載器
//獲取線程上下文類加載器钠绍,獲取這個線程是由哪個類加載器加載的舆声,如果是沒有修改線程上下文類加載器的情況下,則保持與父線程同樣的類加載器
public ClassLoader getContextClassLoader() {
if (contextClassLoader == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(contextClassLoader,
Reflection.getCallerClass());
}
return contextClassLoader;
}
// 設(shè)置線程上下文類加載器:制定類加載器加載
public void setContextClassLoader(ClassLoader cl) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setContextClassLoader"));
}
contextClassLoader = cl;
}
8. 線程的join方法
Thread類中提供了三個join方法柳爽,最終使用的是第三個join方法媳握,如下:
public final void join() throws InterruptedException {
join(0);
}
===================================================================
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
===================================================================
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
由上面的源碼可以看出,在使用join時磷脯,調(diào)用的是wait方法去阻塞線程蛾找,而我們知道,wait方法會釋放對象鎖赵誓,這是join方法和sleep方法的不同之處打毛。join方法可以理解為柿赊,線程排隊(duì)執(zhí)行,比如有A幻枉、B碰声、C三個線程,分別執(zhí)行了join方法之后展辞,會以此執(zhí)行奥邮,代碼如下:
public class ThreadStu {
public static void main(String[] args) throws Exception {
// join
join();
}
private static void join() throws InterruptedException {
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < 3; j++) {
try {
System.out.println(Thread.currentThread().getName() + "#" + j);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
thread.join();
}
}
}
執(zhí)行結(jié)果如下:
Thread-0#0
Thread-0#1
Thread-0#2
Thread-1#0
Thread-1#1
Thread-1#2