国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術(shù)文章
文章詳情頁

詳解JAVA 線程-線程的狀態(tài)有哪些?它是如何工作的?

瀏覽:64日期:2022-08-30 17:39:00

線程(Thread)是并發(fā)編程的基礎(chǔ),也是程序執(zhí)行的最小單元,它依托進(jìn)程而存在。

一個進(jìn)程中可以包含多個線程,多線程可以共享一塊內(nèi)存空間和一組系統(tǒng)資源,因此線程之間的切換更加節(jié)省資源、更加輕量化,也因此被稱為輕量級的進(jìn)程。

線程的狀態(tài)在 JDK 1.5 之后以枚舉的方式被定義在 Thread 的源碼中,它總共包含以下 6 個狀態(tài):

NEW,新建狀態(tài),線程被創(chuàng)建出來,但尚未啟動時的線程狀態(tài); RUNNABLE,就緒狀態(tài),表示可以運行的線程狀態(tài),它可能正在運行,或者是在排隊等待操作系統(tǒng)給它分配 CPU 資源; BLOCKED,阻塞等待鎖的線程狀態(tài),表示處于阻塞狀態(tài)的線程正在等待監(jiān)視器鎖,比如等待執(zhí)行 synchronized 代碼塊或者使用 synchronized 標(biāo)記的方法; WAITING,等待狀態(tài),一個處于等待狀態(tài)的線程正在等待另一個線程執(zhí)行某個特定的動作,比如,一個線程調(diào)用了 Object.wait() 方法,那它就在等待另一個線程調(diào)用 Object.notify() 或 Object.notifyAll() 方法; TIMED_WAITING,計時等待狀態(tài),和等待狀態(tài)(WAITING)類似,它只是多了超時時間,比如調(diào)用了有超時時間設(shè)置的方法 Object.wait(long timeout) 和 Thread.join(long timeout) 等這些方法時,它才會進(jìn)入此狀態(tài); TERMINATED,終止?fàn)顟B(tài),表示線程已經(jīng)執(zhí)行完成。

線程狀態(tài)的源代碼如下:

public enum State { /** * 新建狀態(tài),線程被創(chuàng)建出來,但尚未啟動時的線程狀態(tài) */ NEW, /** * 就緒狀態(tài),表示可以運行的線程狀態(tài),但它在排隊等待來自操作系統(tǒng)的 CPU 資源 */ RUNNABLE, /** * 阻塞等待鎖的線程狀態(tài),表示正在處于阻塞狀態(tài)的線程 * 正在等待監(jiān)視器鎖,比如等待執(zhí)行 synchronized 代碼塊或者 * 使用 synchronized 標(biāo)記的方法 */ BLOCKED, /** * 等待狀態(tài),一個處于等待狀態(tài)的線程正在等待另一個線程執(zhí)行某個特定的動作。 * 例如,一個線程調(diào)用了 Object.wait() 它在等待另一個線程調(diào)用 * Object.notify() 或 Object.notifyAll() */ WAITING, /** * 計時等待狀態(tài),和等待狀態(tài) (WAITING) 類似,只是多了超時時間,比如 * 調(diào)用了有超時時間設(shè)置的方法 Object.wait(long timeout) 和 * Thread.join(long timeout) 就會進(jìn)入此狀態(tài) */ TIMED_WAITING, /** * 終止?fàn)顟B(tài),表示線程已經(jīng)執(zhí)行完成 */}

線程的工作模式是,首先先要創(chuàng)建線程并指定線程需要執(zhí)行的業(yè)務(wù)方法,然后再調(diào)用線程的 start() 方法,此時線程就從 NEW(新建)狀態(tài)變成了 RUNNABLE(就緒)狀態(tài);

然后線程會判斷要執(zhí)行的方法中有沒有 synchronized 同步代碼塊,如果有并且其他線程也在使用此鎖,那么線程就會變?yōu)?BLOCKED(阻塞等待)狀態(tài),當(dāng)其他線程使用完此鎖之后,線程會繼續(xù)執(zhí)行剩余的方法。

當(dāng)遇到 Object.wait() 或 Thread.join() 方法時,線程會變?yōu)?WAITING(等待狀態(tài))狀態(tài);

如果是帶了超時時間的等待方法,那么線程會進(jìn)入 TIMED_WAITING(計時等待)狀態(tài);

當(dāng)有其他線程執(zhí)行了 notify() 或 notifyAll() 方法之后,線程被喚醒繼續(xù)執(zhí)行剩余的業(yè)務(wù)方法,直到方法執(zhí)行完成為止,此時整個線程的流程就執(zhí)行完了,執(zhí)行流程如下圖所示:

詳解JAVA 線程-線程的狀態(tài)有哪些?它是如何工作的?

【BLOCKED 和 WAITING 的區(qū)別】

雖然 BLOCKED 和 WAITING 都有等待的含義,但二者有著本質(zhì)的區(qū)別。

首先它們狀態(tài)形成的調(diào)用方法不同。

其次 BLOCKED 可以理解為當(dāng)前線程還處于活躍狀態(tài),只是在阻塞等待其他線程使用完某個鎖資源;

而 WAITING 則是因為自身調(diào)用了 Object.wait() 或著是 Thread.join() 又或者是 LockSupport.park() 而進(jìn)入等待狀態(tài),只能等待其他線程執(zhí)行某個特定的動作才能被繼續(xù)喚醒。

比如當(dāng)線程因為調(diào)用了 Object.wait() 而進(jìn)入 WAITING 狀態(tài)之后,則需要等待另一個線程執(zhí)行 Object.notify() 或 Object.notifyAll() 才能被喚醒。

【start() 和 run() 的區(qū)別】

首先從 Thread 源碼來看,start() 方法屬于 Thread 自身的方法,并且使用了 synchronized 來保證線程安全,源碼如下:

public synchronized void start() { // 狀態(tài)驗證,不等于 NEW 的狀態(tài)會拋出異常 if (threadStatus != 0) throw new IllegalThreadStateException(); // 通知線程組,此線程即將啟動 group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { // 不處理任何異常,如果 start0 拋出異常,則它將被傳遞到調(diào)用堆棧上 } }}

run() 方法為 Runnable 的抽象方法,必須由調(diào)用類重寫此方法,重寫的 run() 方法其實就是此線程要執(zhí)行的業(yè)務(wù)方法,源碼如下:

public class Thread implements Runnable { // 忽略其他方法...... private Runnable target; @Override public void run() { if (target != null) { target.run(); } }}@FunctionalInterfacepublic interface Runnable { public abstract void run();}

從執(zhí)行的效果來說,start() 方法可以開啟多線程,讓線程從 NEW 狀態(tài)轉(zhuǎn)換成 RUNNABLE 狀態(tài),而 run() 方法只是一個普通的方法。

其次,它們可調(diào)用的次數(shù)不同,start() 方法不能被多次調(diào)用,否則會拋出 java.lang.IllegalStateException;而 run() 方法可以進(jìn)行多次調(diào)用,因為它只是一個普通的方法而已。

【線程優(yōu)先級】

在 Thread 源碼中和線程優(yōu)先級相關(guān)的屬性有 3 個:

// 線程可以擁有的最小優(yōu)先級public final static int MIN_PRIORITY = 1;// 線程默認(rèn)優(yōu)先級public final static int NORM_PRIORITY = 5;// 線程可以擁有的最大優(yōu)先級public final static int MAX_PRIORITY = 10

線程的優(yōu)先級可以理解為線程搶占 CPU 時間片的概率,優(yōu)先級越高的線程優(yōu)先執(zhí)行的概率就越大,但并不能保證優(yōu)先級高的線程一定先執(zhí)行。

在程序中我們可以通過 Thread.setPriority() 來設(shè)置優(yōu)先級,setPriority() 源碼如下:

public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); // 先驗證優(yōu)先級的合理性 if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { // 優(yōu)先級如果超過線程組的最高優(yōu)先級,則把優(yōu)先級設(shè)置為線程組的最高優(yōu)先級 if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); }}

【線程的常用方法】

線程的常用方法有以下幾個。

join()

在一個線程中調(diào)用 other.join() ,這時候當(dāng)前線程會讓出執(zhí)行權(quán)給 other 線程,直到 other 線程執(zhí)行完或者過了超時時間之后再繼續(xù)執(zhí)行當(dāng)前線程,join() 源碼如下:

public final synchronized void join(long millis)throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; // 超時時間不能小于 0 if (millis < 0) { throw new IllegalArgumentException('timeout value is negative'); } // 等于 0 表示無限等待,直到線程執(zhí)行完為之 if (millis == 0) { // 判斷子線程 (其他線程) 為活躍線程,則一直等待 while (isAlive()) { wait(0); } } else { // 循環(huán)判斷 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } }}

從源碼中可以看出 join() 方法底層還是通過 wait() 方法來實現(xiàn)的。

例如,在未使用 join() 時,代碼如下:

public class ThreadExample { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { for (int i = 1; i < 6; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println('子線程睡眠:' + i + '秒。'); } }); thread.start(); // 開啟線程 // 主線程執(zhí)行 for (int i = 1; i < 4; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println('主線程睡眠:' + i + '秒。'); } }}

程序執(zhí)行結(jié)果為:

復(fù)制主線程睡眠:1秒。

子線程睡眠:1秒。

主線程睡眠:2秒。

子線程睡眠:2秒。

主線程睡眠:3秒。

子線程睡眠:3秒。

子線程睡眠:4秒。

子線程睡眠:5秒。

從結(jié)果可以看出,在未使用 join() 時主子線程會交替執(zhí)行。

然后我們再把 join() 方法加入到代碼中,代碼如下:

public class ThreadExample { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { for (int i = 1; i < 6; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println('子線程睡眠:' + i + '秒。'); } }); thread.start(); // 開啟線程 thread.join(2000); // 等待子線程先執(zhí)行 2 秒鐘 // 主線程執(zhí)行 for (int i = 1; i < 4; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println('主線程睡眠:' + i + '秒。'); } }}

程序執(zhí)行結(jié)果為:

復(fù)制子線程睡眠:1秒。

子線程睡眠:2秒。

主線程睡眠:1秒。

// thread.join(2000); 等待 2 秒之后,主線程和子線程再交替執(zhí)行

子線程睡眠:3秒。

主線程睡眠:2秒。

子線程睡眠:4秒。

子線程睡眠:5秒。

主線程睡眠:3秒。

從執(zhí)行結(jié)果可以看出,添加 join() 方法之后,主線程會先等子線程執(zhí)行 2 秒之后才繼續(xù)執(zhí)行。

yield()

看 Thread 的源碼可以知道 yield() 為本地方法,也就是說 yield() 是由 C 或 C++ 實現(xiàn)的,源碼如下:

public static native void yield();

yield() 方法表示給線程調(diào)度器一個當(dāng)前線程愿意出讓 CPU 使用權(quán)的暗示,但是線程調(diào)度器可能會忽略這個暗示。

比如我們執(zhí)行這段包含了 yield() 方法的代碼,如下所示:

public static void main(String[] args) throws InterruptedException { Runnable runnable = new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println('線程:' + Thread.currentThread().getName() + ' I:' + i); if (i == 5) { Thread.yield(); } } } }; Thread t1 = new Thread(runnable, 'T1'); Thread t2 = new Thread(runnable, 'T2'); t1.start(); t2.start();}

當(dāng)我們把這段代碼執(zhí)行多次之后會發(fā)現(xiàn),每次執(zhí)行的結(jié)果都不相同,這是因為 yield() 執(zhí)行非常不穩(wěn)定,線程調(diào)度器不一定會采納 yield() 出讓 CPU 使用權(quán)的建議,從而導(dǎo)致了這樣的結(jié)果。

以上就是詳解JAVA 線程-線程的狀態(tài)有哪些?它是如何工作的?的詳細(xì)內(nèi)容,更多關(guān)于java 線程的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 青青自拍视频一区二区三区 | 国产一级一片免费播放视频 | 久久精品国产一区二区三区不卡 | 国产成人精品一区二区三在线观看 | 国产女人伦码一区二区三区不卡 | 国产麻豆交换夫妇 | 国产做a爰片久久毛片 | 日本一区二区高清不卡 | 国产美女做爰免费视频软件 | 欧美国产在线视频 | 免费一级欧美毛片 | 欧美2区| 99精品视频观看 | 在线国产一区二区 | 久草网视频在线观看 | 亚洲综合综合在线 | 欧美成人高清视频 | 亚洲精品成人久久久影院 | 亚洲国产一 | 国产亚洲精品成人婷婷久久小说 | 手机在线观看毛片 | 青青热久久国产久精品秒播 | 午夜毛片不卡高清免费 | 日韩精品午夜视频一区二区三区 | 99免费在线观看视频 | 国产午夜爽爽窝窝在线观看 | 麻豆69堂免费视频 | 最新亚洲精品 | 香蕉福利久久福利久久香蕉 | 亚洲伊人色 | 成人一级大片 | 日韩欧美精品在线观看 | 亚洲精品免费在线观看 | 波多野结衣一区二区在线 | 久久精品99精品免费观看 | 亚洲国产精品综合久久20 | 一区二区三区在线免费看 | 国产一区二区免费在线观看 | yellow中文字幕久久网 | 亚洲精品免费在线观看 | 成年日韩片av在线网站 |