概述
線程的狀態(tài)
Java 中線程中狀態(tài)可分為五種:New(新建狀態(tài))鲤看,Runnable(就緒狀態(tài)),Running(運(yùn)行狀態(tài))耍群,Blocked(阻塞狀態(tài))义桂,Dead(死亡狀態(tài))
New:新建狀態(tài)找筝,當(dāng)線程創(chuàng)建完成時(shí)為新建狀態(tài),即new Thread(...)慷吊,還沒(méi)有調(diào)用start方法時(shí)袖裕,線程處于新建狀態(tài)。
Runnable:就緒狀態(tài)溉瓶,當(dāng)調(diào)用線程的的start方法后急鳄,線程進(jìn)入就緒狀態(tài),等待CPU資源堰酿。處于就緒狀態(tài)的線程由Java運(yùn)行時(shí)系統(tǒng)的線程調(diào)度程序(thread scheduler)來(lái)調(diào)度疾宏。
Running:運(yùn)行狀態(tài),就緒狀態(tài)的線程獲取到CPU執(zhí)行權(quán)以后進(jìn)入運(yùn)行狀態(tài)触创,開(kāi)始執(zhí)行run方法坎藐。
Blocked:阻塞狀態(tài),線程沒(méi)有執(zhí)行完哼绑,由于某種原因(如岩馍,I/O操作等)讓出CPU執(zhí)行權(quán),自身進(jìn)入阻塞狀態(tài)抖韩。
Dead:死亡狀態(tài)蛀恩,線程執(zhí)行完成或者執(zhí)行過(guò)程中出現(xiàn)異常,線程就會(huì)進(jìn)入死亡狀態(tài)茂浮。
這五種狀態(tài)之間的轉(zhuǎn)換關(guān)系如下圖所示
Java中是如何實(shí)現(xiàn)這幾種狀態(tài)的轉(zhuǎn)換的
通過(guò) wait/notify/notifyAll 方法的使用
這三個(gè)方法都是在 Object 對(duì)象中定義的双谆,這里不做講解,具體可看 深入理解Object類的 wait 和 notify 這篇文章
通過(guò) sleep/yield/join 方法的使用
這三個(gè)方法都是在 Thread 對(duì)象中定義的励稳,本篇詳細(xì)講解
Thread類中常用的方法
start方法
start()用來(lái)啟動(dòng)一個(gè)線程佃乘,當(dāng)調(diào)用start方法后囱井,系統(tǒng)才會(huì)開(kāi)啟一個(gè)新的線程來(lái)執(zhí)行用戶定義的子任務(wù)驹尼,在這個(gè)過(guò)程中,會(huì)為相應(yīng)的線程分配需要的資源
run方法
run()方法是不需要用戶來(lái)調(diào)用的庞呕,當(dāng)通過(guò)start方法啟動(dòng)一個(gè)線程之后新翎,當(dāng)線程獲得了CPU執(zhí)行時(shí)間,便進(jìn)入run方法體去執(zhí)行具體的任務(wù)住练。注意地啰,繼承Thread類必須重寫run方法,在run方法中定義具體要執(zhí)行的任務(wù)
sleep方法
sleep相當(dāng)于讓線程睡眠讲逛,交出CPU亏吝,讓CPU去執(zhí)行其他的任務(wù)。
但是有一點(diǎn)要非常注意盏混,sleep方法不會(huì)釋放鎖蔚鸥,也就是說(shuō)如果當(dāng)前線程持有對(duì)某個(gè)對(duì)象的鎖惜论,則即使調(diào)用sleep方法,其他線程也無(wú)法訪問(wèn)這個(gè)對(duì)象止喷。
注意馆类,如果調(diào)用了sleep方法,必須捕獲InterruptedException異车或者將該異常向上層拋出乾巧。當(dāng)線程睡眠時(shí)間滿后,不一定會(huì)立即得到執(zhí)行预愤,因?yàn)榇藭r(shí)可能CPU正在執(zhí)行其他的任務(wù)沟于。所以說(shuō)調(diào)用sleep方法相當(dāng)于讓線程進(jìn)入阻塞狀態(tài)。
yield方法
調(diào)用yield方法會(huì)讓當(dāng)前線程交出CPU權(quán)限植康,讓CPU去執(zhí)行其他的線程社裆。它跟sleep方法類似,同樣不會(huì)釋放鎖向图。但是yield不能控制具體的交出CPU的時(shí)間泳秀,另外,yield方法只能讓擁有相同優(yōu)先級(jí)的線程有獲取CPU執(zhí)行時(shí)間的機(jī)會(huì)榄攀。
注意嗜傅,調(diào)用yield方法并不會(huì)讓線程進(jìn)入阻塞狀態(tài),而是讓線程重回就緒狀態(tài)檩赢,它只需要等待重新獲取CPU執(zhí)行時(shí)間吕嘀,這一點(diǎn)是和sleep方法不一樣的。
join方法
假如在main線程中贞瞒,調(diào)用thread.join方法偶房,則main方法會(huì)等待thread線程執(zhí)行完畢或者等待一定的時(shí)間。如果調(diào)用的是無(wú)參join方法军浆,則等待thread執(zhí)行完畢棕洋,如果調(diào)用的是指定了時(shí)間參數(shù)的join方法,則等待一定的時(shí)間乒融。
實(shí)際上調(diào)用join方法是調(diào)用了Object的wait方法掰盘,這個(gè)可以通過(guò)查看源碼得知
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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;
}
}
}
wait方法會(huì)讓線程進(jìn)入阻塞狀態(tài),并且會(huì)釋放線程占有的鎖赞季,并交出CPU執(zhí)行權(quán)限愧捕。由于wait方法會(huì)讓線程釋放對(duì)象鎖,所以join方法同樣會(huì)讓線程釋放對(duì)一個(gè)對(duì)象持有的鎖申钩。
這里可能有一個(gè)疑問(wèn)次绘,wait 方法調(diào)用后為什么沒(méi)有看到調(diào)用 notify 呢?
通過(guò)上面源碼的第六行注釋(As a thread terminates the {@code this.notifyAll} method is invoked)可以知道,實(shí)際上是在此線程上調(diào)用了需要加入的線程對(duì)象的wait()方法邮偎,加入的線程運(yùn)行完后罗洗,會(huì)自動(dòng)調(diào)用 notifyAll 方法,被 join 的線程自然能夠繼續(xù)執(zhí)行了钢猛。
interrupt方法
interrupt伙菜,顧名思義,即中斷的意思命迈。
單獨(dú)調(diào)用interrupt方法可以使得處于阻塞狀態(tài)的線程拋出一個(gè)異常贩绕,也就說(shuō),它可以用來(lái)中斷一個(gè)正處于阻塞狀態(tài)的線程壶愤;
直接調(diào)用interrupt方法不能中斷正在運(yùn)行中的線程淑倾;
如果配合isInterrupted()能夠中斷正在運(yùn)行的線程,因?yàn)檎{(diào)用interrupt方法相當(dāng)于將中斷標(biāo)志位置為true征椒,那么可以通過(guò)調(diào)用isInterrupted()判斷中斷標(biāo)志是否被置位來(lái)中斷線程的執(zhí)行娇哆。比如下面這段代碼:
public class Test {
public static void main(String[] args) throws IOException {
Test test = new Test();
MyThread thread = test.new MyThread();
thread.start();
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
}
thread.interrupt();
}
class MyThread extends Thread{
@Override
public void run() {
int i = 0;
while(!isInterrupted() && i<Integer.MAX_VALUE){
System.out.println(i+" while循環(huán)");
i++;
}
}
}
}
Thread類的一些屬性
Thread類實(shí)現(xiàn)了Runnable接口,在Thread類中勃救,有一些比較關(guān)鍵的屬性碍讨,比如name是表示Thread的名字,可以通過(guò)Thread類的構(gòu)造器中的參數(shù)來(lái)指定線程名字蒙秒,priority表示線程的優(yōu)先級(jí)(最大值為10勃黍,最小值為1,默認(rèn)值為5)晕讲,daemon表示線程是否是守護(hù)線程覆获,target表示要執(zhí)行的任務(wù)。
關(guān)于線程的優(yōu)先級(jí) priority
對(duì)于優(yōu)先級(jí)設(shè)置的內(nèi)容可以通過(guò)Thread類的幾個(gè)常量來(lái)決定
- 最高優(yōu)先級(jí):public final static int MAX_PRIORITY = 10;
- 中等優(yōu)先級(jí):public final static int NORM_PRIORITY = 5;
- 最低優(yōu)先級(jí):public final static int MIN_PRIORITY = 1;
新創(chuàng)建的線程的優(yōu)先級(jí)就是普通優(yōu)先級(jí) Thread.NORM_PRIORITY
繼承性:Java 默認(rèn)的線程優(yōu)先級(jí)是父線程的優(yōu)先級(jí)瓢省,也就是如果此時(shí)在線程A中啟動(dòng)一個(gè)線程B弄息,那么B和A的優(yōu)先級(jí)將是一樣的
高優(yōu)先級(jí)的線程比低優(yōu)先級(jí)的線程有更高的幾率得到執(zhí)行,實(shí)際上這和操作系統(tǒng)及虛擬機(jī)版本相關(guān)勤婚,有可能即使設(shè)置了線程的優(yōu)先級(jí)也不會(huì)產(chǎn)生任何作用
關(guān)于守護(hù)線程 daemon
- Java中有兩種線程:用戶線程和守護(hù)線程
- 用戶線程和守護(hù)線程的區(qū)別:通過(guò)調(diào)用isDaemon()方法辨別摹量,返回false的為"用戶線程",否則為"守護(hù)線程"
- 典型的守護(hù)線程是垃圾回收線程蛔六。只要當(dāng)前JVM進(jìn)程中存在任何一個(gè)還未結(jié)束的非守護(hù)線程荆永,守護(hù)線程就會(huì)一直工作,只有當(dāng)最后一個(gè)非守護(hù)線程結(jié)束時(shí)国章,守護(hù)線程才會(huì)隨著JVM一起停止工作
- 主線程main為用戶線程
總結(jié)
線程各個(gè)狀態(tài)之間的轉(zhuǎn)化情況
sleep和yield有什么異同?
- 二者都不會(huì)釋放鎖
- sleep方法相當(dāng)于讓線程進(jìn)入阻塞狀態(tài)豆村,yield方法并不會(huì)讓線程進(jìn)入阻塞狀態(tài)液兽,而是讓線程重回就緒狀
- yield不能控制具體的交出CPU的時(shí)間,另外,yield方法只能讓擁有相同優(yōu)先級(jí)的線程有獲取CPU執(zhí)行時(shí)間的機(jī)會(huì)
sleep和wait有什么異同四啰?
- Thread.sleep()與Object.wait()二者都可以暫停當(dāng)前線程宁玫,釋放CPU控制權(quán)。
- Object.wait()在釋放CPU同時(shí)柑晒,釋放了對(duì)象鎖的控制欧瘪;Thread.sleep()沒(méi)有對(duì)鎖釋放
參考資料
- https://www.cnblogs.com/dolphin0520/p/3920357.html
- https://www.cnblogs.com/w-wfy/p/6414801.html
- https://blog.csdn.net/zhuyong7/article/details/80852884(interrupt、interrupted和isInterrupted的區(qū)別) 匙赞,有待深入理解