成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Thread類(lèi)源碼解讀(2)——線(xiàn)程狀態(tài)及常用方法

luqiuwen / 2649人閱讀

摘要:如果線(xiàn)程還存活,線(xiàn)程就無(wú)限期等待,并讓出監(jiān)視器鎖,進(jìn)入狀態(tài)。當(dāng)線(xiàn)程從狀態(tài)被喚醒后通過(guò),或者是假喚醒將繼續(xù)競(jìng)爭(zhēng)監(jiān)視器鎖,當(dāng)成功獲得監(jiān)視器鎖后,他將從調(diào)用的地方恢復(fù),繼續(xù)運(yùn)行。

前言

系列文章目錄

上一篇我們討論了線(xiàn)程的創(chuàng)建,本篇我們來(lái)聊一聊線(xiàn)程的狀態(tài)轉(zhuǎn)換以及常用的幾個(gè)比較重要的方法。

本篇依然是通過(guò)源碼分析來(lái)了解這些知識(shí)。

本文源碼基于jdk1.8 。

閱讀完本文,你應(yīng)當(dāng)有能力回答以下常見(jiàn)面試題:

線(xiàn)程有哪幾種狀態(tài)以及各種狀態(tài)之間的轉(zhuǎn)換?

Thread.sleep() 與 Thread.currentThread().sleep() 有什么區(qū)別?

Thread.sleep() 和 Object#wait() 有什么區(qū)別?

Thread.sleep() 和 Thread.yield()有什么區(qū)別?

說(shuō)說(shuō)你對(duì)join方法的理解?

線(xiàn)程狀態(tài)

在Thread類(lèi)中, 線(xiàn)程狀態(tài)是通過(guò)threadStatus屬性以及State枚舉類(lèi)實(shí)現(xiàn)的:

/* Java thread status for tools,
 * initialized to indicate thread "not yet started"
 */
private volatile int threadStatus = 0;


public enum State {
    /**
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE,

    /**
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * 
    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; } /** * Returns the state of this thread. * This method is designed for use in monitoring of the system state, * not for synchronization control. * * @return this thread"s state. * @since 1.5 */ public State getState() { // get current thread state return sun.misc.VM.toThreadState(threadStatus); }

從源碼中可以看出, 線(xiàn)程一共有6種狀態(tài), 其狀態(tài)轉(zhuǎn)換關(guān)系如下圖所示:

值得一提的是,從狀態(tài)的定義中可以看出,RUNNABLE狀態(tài)包含了我們通常所說(shuō)的runningready兩種狀態(tài)。

常用方法 currentThread

源碼中currentThread定義如下:

/**
 * Returns a reference to the currently executing thread object.
 *
 * @return  the currently executing thread.
 */
public static native Thread currentThread();

可見(jiàn),它是一個(gè)靜態(tài)方法,并且是一個(gè)native方法,返回的是當(dāng)前正在執(zhí)行的線(xiàn)程。

愛(ài)思考的同學(xué)可能就要問(wèn)了,現(xiàn)在咱都多核CPU了,同一時(shí)刻可以有多個(gè)線(xiàn)程跑在不同的CPU核心上,那當(dāng)前正在執(zhí)行的線(xiàn)程有多個(gè),到底返回的是哪一個(gè)呢?

其實(shí),這里"當(dāng)前正在執(zhí)行的線(xiàn)程"指的是當(dāng)前正在執(zhí)行這段代碼的線(xiàn)程。

我們知道,線(xiàn)程是CPU調(diào)度的最小單位,任意一段代碼總得由一個(gè)線(xiàn)程執(zhí)行,所以該方法返回的是正在執(zhí)行Thread.currentThread這行代碼的線(xiàn)程,例如:

public class ThreadTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }
}

輸出:

main

我們知道當(dāng)一個(gè)Java程序啟動(dòng)以后,有一個(gè)線(xiàn)程就會(huì)立馬跑起來(lái),這就是通常所說(shuō)的Main線(xiàn)程,main線(xiàn)程將會(huì)執(zhí)行java的入口方法main方法,所以當(dāng)前正在執(zhí)行Thread.currentThread()方法的線(xiàn)程就是main線(xiàn)程。

sleep

談起sleep方法, 被問(wèn)的最多的兩個(gè)問(wèn)題就是:

Thread.sleep() 與 Thread.currentThread().sleep() 有什么區(qū)別?

Thread.sleep() 和 Object.wait()有什么區(qū)別?

這些問(wèn)題的答案, 你在源碼里都能找得到。我們直接來(lái)看源碼:

/**
 * Causes the currently executing thread to sleep (temporarily cease
 * execution) for the specified number of milliseconds, subject to
 * the precision and accuracy of system timers and schedulers. The thread
 * does not lose ownership of any monitors.
 *
 * @param  millis
 *         the length of time to sleep in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          interrupted status of the current thread is
 *          cleared when this exception is thrown.
 */
public static native void sleep(long millis) throws InterruptedException;

可見(jiàn), sleep方法也是一個(gè)靜態(tài)方法, 并且是native方法, 從注釋Causes the currently executing thread to sleep中可以看出, 它作用于當(dāng)前正在執(zhí)行的線(xiàn)程, 所以上面那個(gè)問(wèn)題我們就能回答了:

Thread.sleep() 與 Thread.currentThread().sleep() 沒(méi)有區(qū)別

如果硬要說(shuō)他們有什么區(qū)別的話(huà), 那就是一個(gè)是用類(lèi)直接調(diào)用靜態(tài)方法, 一個(gè)是用類(lèi)的實(shí)例調(diào)用靜態(tài)方法.

另外, 上面的注釋中還有一句非常重要的話(huà):

The thread does not lose ownership of any monitors.

也就是說(shuō), 雖然sleep函數(shù)使當(dāng)前線(xiàn)程讓出了CPU, 但是, 當(dāng)前線(xiàn)程仍然持有它所獲得的監(jiān)視器鎖, 這與同時(shí)讓出CPU資源和監(jiān)視器鎖資源的wait方法是不一樣的。

sleep方法還有另外一個(gè)版本:

/**
 * Causes the currently executing thread to sleep (temporarily cease
 * execution) for the specified number of milliseconds plus the specified
 * number of nanoseconds, subject to the precision and accuracy of system
 * timers and schedulers. The thread does not lose ownership of any
 * monitors.
 *
 * @param  millis
 *         the length of time to sleep in milliseconds
 *
 * @param  nanos
 *         {@code 0-999999} additional nanoseconds to sleep
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative, or the value of
 *          {@code nanos} is not in the range {@code 0-999999}
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          interrupted status of the current thread is
 *          cleared when this exception is thrown.
 */
public static void sleep(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++;
    }

    sleep(millis);
}

這個(gè)方法多加了納秒級(jí)別的延時(shí)參數(shù), 但是我們看源碼就知道, 這個(gè)多加的納秒級(jí)別的延時(shí)并沒(méi)有什么用, 最終該函數(shù)還是調(diào)用了上面的單參數(shù)native sleep方法, 延時(shí)還是毫秒級(jí)別的, 多出來(lái)的參數(shù)最多是讓當(dāng)前毫秒級(jí)別的延時(shí)增加1毫秒.
還記得我們上次講的wait方法嗎?我們來(lái)對(duì)比下:

public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }

    if (nanos > 0) {
        timeout++;
    }

    wait(timeout);
}

怎么樣?是不是很像??jī)烧咧徊贿^(guò)在從納秒向毫秒的進(jìn)位處有細(xì)微的差別,我猜這個(gè)不統(tǒng)一是歷史原因?qū)е碌摹?/p>

另外,值得一提的是,wait有無(wú)參的wait()方法,它調(diào)用的是wait(0),表示無(wú)限期等待,而sleep并沒(méi)有無(wú)參數(shù)的版本,那么sleep(0)代表什么呢?

這一點(diǎn)在源碼里面并沒(méi)有提及,但是通過(guò)猜測(cè)sleep方法的定義我們知道,它是讓出CPU 0毫秒,這聽(tīng)上去好像沒(méi)有什么意義,但其實(shí)調(diào)用Thread.sleep(0)的當(dāng)前線(xiàn)程確實(shí)被“凍結(jié)”了一下,讓其他線(xiàn)程有機(jī)會(huì)優(yōu)先執(zhí)行。也就是說(shuō)當(dāng)前線(xiàn)程會(huì)釋放一些未用完的時(shí)間片給其他線(xiàn)程或進(jìn)程使用,就相當(dāng)于一個(gè)讓位動(dòng)作,這看上去就和下面要說(shuō)的yield方法很像了。

yield

既然上面談到了sleep(0)方法, 就不得不提yield方法了:

/**
 * A hint to the scheduler that the current thread is willing to yield
 * its current use of a processor. The scheduler is free to ignore this
 * hint.
 *
 * 

Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * *

It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ public static native void yield();

yield方法也是一個(gè)native方法, 從它的注釋可以看出A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint. 它對(duì)于CPU只是一個(gè)建議, 告訴CPU, 當(dāng)前線(xiàn)程愿意讓出CPU給其他線(xiàn)程使用, 至于CPU采不采納, 取決于不同廠(chǎng)商的行為, 有可能一個(gè)線(xiàn)程剛yield出CPU, 然后又立馬獲得了CPU。與之相對(duì), sleep方法一定會(huì)讓出CPU資源, 并且休眠指定的時(shí)間, 不參與CPU的競(jìng)爭(zhēng).

所以調(diào)用yield方法不會(huì)使線(xiàn)程退出RUNNANLE狀態(tài),頂多會(huì)使線(xiàn)程從running 變成 ready,
但是sleep方法是有可能將線(xiàn)程狀態(tài)轉(zhuǎn)換成TIMED_WAITING的。

isAlive

isAlive方法用于檢查線(xiàn)程是否還活著,它是一個(gè)native方法,但不是靜態(tài)方法,也就是說(shuō)它必須被線(xiàn)程的實(shí)例所調(diào)用。

其實(shí)大家可以思考一下它為什么不是靜態(tài)方法,因?yàn)殪o態(tài)方法一般都是作用于當(dāng)前正在執(zhí)行的線(xiàn)程,既然是“當(dāng)前正在執(zhí)行”,那必然是Alive的,所以作為靜態(tài)方法調(diào)用并沒(méi)有意義。

/**
 * Tests if this thread is alive. A thread is alive if it has
 * been started and has not yet died.
 *
 * @return  true if this thread is alive;
 *          false otherwise.
 */
public final native boolean isAlive();
join

join方法是另一個(gè)能將線(xiàn)程狀態(tài)轉(zhuǎn)換成WAITING或者TIMED_WAITING的,它和wait方法一樣,有三個(gè)版本,我們一個(gè)個(gè)來(lái)看:

/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * 

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 * interrupted status 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; } } }

這段源碼注釋的開(kāi)頭部分就告訴了我們join方法的作用:

Waits at most {@code millis} milliseconds for this thread to die. A timeout of {@code 0} means to wait forever.

也就是說(shuō),該方法等待this thread終止,最多等指定的時(shí)間,如果指定時(shí)間為0,則一直等。

這里有兩個(gè)問(wèn)題需要弄清楚:

誰(shuí)在等this thread終止?

this thread指的是哪個(gè)線(xiàn)程?

為了便于說(shuō)明,我們直接來(lái)看一個(gè)例子:

public class JoinMethodTest {

    private static void printWithThread(String content) {
        System.out.println("[" + Thread.currentThread().getName() + "線(xiàn)程]: " + content);
    }

    public static void main(String[] args) {

        printWithThread("開(kāi)始執(zhí)行main方法");

        Thread myThread = new Thread(() -> {
            printWithThread("我在自定義的線(xiàn)程的run方法里");
            printWithThread("我馬上要休息1秒鐘, 并讓出CPU給別的線(xiàn)程使用.");
            try {
                Thread.sleep(1000);
                printWithThread("已經(jīng)休息了1秒, 又重新獲得了CPU");
                printWithThread("我休息好了, 馬上就退出了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        try {
            myThread.start();
            printWithThread("我在main方法里面, 我要等下面這個(gè)線(xiàn)程執(zhí)行完了才能繼續(xù)往下執(zhí)行.");
            myThread.join();
            printWithThread("我在main方法里面, 馬上就要退出了.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上面的例子中,我們?cè)趍ain方法中調(diào)用了 myThread.join(),注意上面這段代碼有兩個(gè)線(xiàn)程,一個(gè)是執(zhí)行main方法的線(xiàn)程,一個(gè)是我們自定義的myThread線(xiàn)程,所以上面的兩個(gè)問(wèn)題的答案是:

main線(xiàn)程在等this thread的終止,因?yàn)槲覀冊(cè)趍ain方法中調(diào)用了myThread.join()

this thread線(xiàn)程指的是myThread線(xiàn)程,因?yàn)槲覀冊(cè)趍yThread對(duì)象上調(diào)用了join方法。

上面這段代碼的執(zhí)行結(jié)果為:

[main線(xiàn)程]: 開(kāi)始執(zhí)行main方法
[main線(xiàn)程]: 我在main方法里面, 我要等下面這個(gè)線(xiàn)程執(zhí)行完了才能繼續(xù)往下執(zhí)行.
[Thread-0線(xiàn)程]: 我在自定義的線(xiàn)程的run方法里
[Thread-0線(xiàn)程]: 我馬上要休息1秒鐘, 并讓出CPU給別的線(xiàn)程使用.
[Thread-0線(xiàn)程]: 已經(jīng)休息了1秒, 又重新獲得了CPU
[Thread-0線(xiàn)程]: 我休息好了, 馬上就退出了
[main線(xiàn)程]: 我在main方法里面, 馬上就要退出了.

從運(yùn)行結(jié)果可以看出,雖然myThread線(xiàn)程(即Thread-0線(xiàn)程)中途讓出了CPU, main線(xiàn)程還是必須等到其執(zhí)行完畢了才能繼續(xù)往下執(zhí)行,我們現(xiàn)在修改一下代碼,讓main線(xiàn)程最多等0.5秒,即將myThread.join()改為myThread.join(500);,則結(jié)果如下:

[main線(xiàn)程]: 開(kāi)始執(zhí)行main方法
[main線(xiàn)程]: 我在main方法里面, 我要等下面這個(gè)線(xiàn)程執(zhí)行完了才能繼續(xù)往下執(zhí)行.
[Thread-0線(xiàn)程]: 我在自定義的線(xiàn)程的run方法里
[Thread-0線(xiàn)程]: 我馬上要休息1秒鐘, 并讓出CPU給別的線(xiàn)程使用.
[main線(xiàn)程]: 我在main方法里面, 馬上就要退出了.
[Thread-0線(xiàn)程]: 已經(jīng)休息了1秒, 又重新獲得了CPU
[Thread-0線(xiàn)程]: 我休息好了, 馬上就退出了

我們看到,由于main線(xiàn)程最多等待myThread 0.5秒,在myThread休眠的一秒內(nèi),它就不等了,繼續(xù)往下執(zhí)行,而隨后myThread搶占到CPU資源繼續(xù)運(yùn)行。

通過(guò)列子有了感性的認(rèn)識(shí)后,我們?cè)賮?lái)看源碼,首先看join(0)部分:

public final synchronized void join(long millis) throws InterruptedException {
    ...
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        ...
    }
    ...
}

這是一個(gè)自旋操作,注意,這里的isAlivewait(0)方法都是線(xiàn)程實(shí)例的方法,在上面的例子中就是myThread的方法,Thread雖然是一個(gè)線(xiàn)程類(lèi),但只是特殊在它的native方法上,除此之外,它就是個(gè)普通的java類(lèi),而java中所有的類(lèi)都繼承自Object類(lèi),所以Thread類(lèi)繼承了Object的wait方法,myThread作為線(xiàn)程類(lèi)的實(shí)例,自然也有wait方法。

我們之前說(shuō)wait方法的時(shí)候提到過(guò),執(zhí)行wait方法必須拿到監(jiān)視器鎖,并且必須在同步代碼塊中調(diào)用,這里我們檢查join方法發(fā)現(xiàn),它確實(shí)被synchronized關(guān)鍵字修飾,并且是一個(gè)非靜態(tài)方法,所以它使用的是當(dāng)前對(duì)象實(shí)例的監(jiān)視器鎖(this)。

好像開(kāi)始復(fù)雜了,我們從頭到尾捋一捋(注意了!敲黑板了!這段比較繞! ):

首先我們要明確,這里牽涉到兩個(gè)線(xiàn)程,一個(gè)是main線(xiàn)程,一個(gè)是我們自定義的myThread線(xiàn)程(即例子里的Thread-0)。

我們?cè)趍ain方法中調(diào)用了myThread.join(),main方法由main線(xiàn)程執(zhí)行,所以執(zhí)行myThread.join()這行代碼的“當(dāng)前線(xiàn)程”是main線(xiàn)程。

join方法是一個(gè)同步方法,使用的是對(duì)象鎖(this 鎖),即myThread對(duì)象所關(guān)聯(lián)的監(jiān)視器對(duì)象。

main線(xiàn)程必須首先拿到j(luò)oin方法的監(jiān)視器鎖才能進(jìn)入同步代碼塊。

main線(xiàn)程進(jìn)入同步代碼塊后會(huì)首先檢查myThread線(xiàn)程是否還存活,注意,這里的isAlive是myThread線(xiàn)程的方法,它是檢查myThread線(xiàn)程是否還活著,而不是當(dāng)前線(xiàn)程(當(dāng)前線(xiàn)程是執(zhí)行isAlive方法的線(xiàn)程,即main線(xiàn)程)。

如果myThread線(xiàn)程還存活,(main線(xiàn)程)就無(wú)限期等待,并讓出監(jiān)視器鎖,進(jìn)入WAITING狀態(tài)。

當(dāng)main線(xiàn)程從WAITING狀態(tài)被喚醒后(通過(guò)notify,notifyAll或者是假喚醒), 將繼續(xù)競(jìng)爭(zhēng)監(jiān)視器鎖,當(dāng)成功獲得監(jiān)視器鎖后,他將從調(diào)用wait的地方恢復(fù),繼續(xù)運(yùn)行。由于wait方法在while循環(huán)中,則它將繼續(xù)檢查myThread線(xiàn)程是否存活,如果還是沒(méi)有終止,則繼續(xù)掛起等待。

可以看出,退出這個(gè)“自旋”狀態(tài)的唯一途徑就是myThread線(xiàn)程終止運(yùn)行(或者有中斷異常拋出)。

有的細(xì)心的同學(xué)可能就要問(wèn)了: 要是沒(méi)有人調(diào)用notify或者notifyAll,也沒(méi)有假喚醒狀態(tài)的發(fā)生,那main線(xiàn)程不就一直被wait(0)方法掛起了嗎?這樣以來(lái)不就連檢測(cè)myThread線(xiàn)程是否存活的機(jī)會(huì)都沒(méi)有嗎?這樣即使myThread終止了,也無(wú)法退出啊。

關(guān)于這一點(diǎn),注釋中其實(shí)是做了解釋的:

As a thread terminates the {@code this.notifyAll} method is invoked.

我們知道,wait(0)方法的監(jiān)視器鎖就是myThread對(duì)象(this), 而當(dāng)myThread終止執(zhí)行時(shí),this.notifyAll會(huì)被調(diào)用,所以所有等待this鎖的線(xiàn)程都會(huì)被喚醒,而main線(xiàn)程就是等待在這個(gè)監(jiān)視器鎖上的線(xiàn)程,因此myThread運(yùn)行結(jié)束時(shí),main線(xiàn)程會(huì)從wait方法處被喚醒。

另外,注釋中還多加了一句:

It is recommended that applications not use {@code wait}, {@code notify}, or {@code notifyAll} on {@code Thread} instances.

這個(gè)推薦還是很有必要的,至于為什么,就給大家留作思考題吧<( ̄︶ ̄)>

不過(guò)我這里再啰嗦一句,一定要分清執(zhí)行代碼的線(xiàn)程和方法所屬的線(xiàn)程類(lèi)所代表的線(xiàn)程!

例如,在上面的例子中:

myThread.join() 是myThread對(duì)象的方法,但是執(zhí)行這個(gè)方法的是main線(xiàn)程;

isAlive() 是myThread對(duì)象的方法,但是執(zhí)行這個(gè)方法的是main線(xiàn)程,而這個(gè)方法檢測(cè)是myThread線(xiàn)程是否活著

wait(0) 是myThread對(duì)象的方法,但是執(zhí)行這個(gè)方法的是main線(xiàn)程,它使得main線(xiàn)程掛起,但是main線(xiàn)程是在myThread對(duì)象代表的monitor上掛起。

這里最重要的是區(qū)分“myThread對(duì)象”和“myThread線(xiàn)程”,myThread對(duì)象有時(shí)候代表了myThread線(xiàn)程,例如myThread對(duì)象的isAlive方法,檢測(cè)的就是它代表的myThread線(xiàn)程是否活著,但是其實(shí)大多數(shù)時(shí)候,myThread對(duì)象就是普通的java對(duì)象,這個(gè)對(duì)象的方法通常也都是由其他線(xiàn)程(例如上面例子中的main線(xiàn)程)來(lái)執(zhí)行的,對(duì)于我們自定義的線(xiàn)程來(lái)說(shuō)(例如上面的myThread線(xiàn)程),通常由它自己執(zhí)行的方法就只有傳進(jìn)入的run方法了。

再回到上面的例子,從上面的分析中可以看出,join(0)方法實(shí)現(xiàn)了一定程度上的線(xiàn)程同步,即當(dāng)前線(xiàn)程只有等join方法所屬的線(xiàn)程對(duì)象所代表的線(xiàn)程終止執(zhí)行了才能繼續(xù)往下執(zhí)行,否則將一直掛起等待。

這一點(diǎn)也說(shuō)明使用join(0)是很危險(xiǎn)的,因?yàn)槿绻?b>myThread線(xiàn)程因?yàn)榈貌坏劫Y源一直被掛起,而main線(xiàn)程又在等待myThread線(xiàn)程終止,則程序永遠(yuǎn)會(huì)停在那里,無(wú)法終止,所以源碼中提供了限時(shí)等待的版本:

public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        ...
        if (millis == 0) {
            ...
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

與無(wú)限期等待不同的是,限時(shí)等待只等待指定時(shí)間,如果指定的時(shí)間到了就直接從循環(huán)中跳出來(lái),使用的wai方法也是限時(shí)wait的版本,定時(shí)時(shí)間到了之后,main線(xiàn)程會(huì)被自動(dòng)喚醒。上面的代碼是自解釋的,我就不再贅述了。

接下來(lái)我們?cè)賮?lái)看看其他兩個(gè)版本的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);
}

可見(jiàn),其他兩個(gè)版本最終調(diào)用的都是我們分析的第一版本,這和wait方法,sleep方法很像,至于為什么wait方法和join方法都提供了無(wú)參方法而sleep方法沒(méi)有,我個(gè)人認(rèn)為是為了保持語(yǔ)義的一致性:

wait()join()分別和wait(0)join(0)等價(jià),他們都代表了無(wú)限期等待,而sleep(0)并不代表無(wú)限期等待,所以sleep方法沒(méi)有無(wú)參的形式,以防止語(yǔ)義上的混亂。除這點(diǎn)之外,這三個(gè)方法在兩個(gè)參數(shù)的版本XXX(long millis, int nanos)中的實(shí)現(xiàn),都大同小異。

另外最后一點(diǎn)值得注意的是,我們?cè)?b>join方法中只調(diào)用了isAlive方法檢測(cè)線(xiàn)程是否存活,并沒(méi)有啟動(dòng)這個(gè)線(xiàn)程,也就是說(shuō),如果我們想要實(shí)現(xiàn)當(dāng)前線(xiàn)程等待myThread線(xiàn)程執(zhí)行完成之后再執(zhí)行的效果,就必須在調(diào)用myThread.join()之前調(diào)用myThread.start()讓線(xiàn)程先跑起來(lái),否則join方法發(fā)現(xiàn)isAlive為false會(huì)立即退出,myThread線(xiàn)程就不會(huì)被執(zhí)行,大家可以將myThread.start()注釋掉自己跑一跑試試看。

(完)

查看更多系列文章:系列文章目錄

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76779.html

相關(guān)文章

  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過(guò)長(zhǎng),于是一些比較大的主題就都分成幾篇來(lái)講了,這篇文章是筆者所有文章的目錄,將會(huì)持續(xù)更新,以給大家一個(gè)查看系列文章的入口。 前言 大家好,筆者是今年才開(kāi)始寫(xiě)博客的,寫(xiě)作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因?yàn)閷?xiě)作的時(shí)候發(fā)現(xiàn),為了弄懂一個(gè)知識(shí),不得不先去了解另外一些知識(shí),這樣以來(lái),為了說(shuō)明一個(gè)問(wèn)題,就要把一系列知識(shí)都了解一遍,寫(xiě)出來(lái)的文章就特別長(zhǎng)。 為了避免一篇...

    lijy91 評(píng)論0 收藏0
  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過(guò)長(zhǎng),于是一些比較大的主題就都分成幾篇來(lái)講了,這篇文章是筆者所有文章的目錄,將會(huì)持續(xù)更新,以給大家一個(gè)查看系列文章的入口。 前言 大家好,筆者是今年才開(kāi)始寫(xiě)博客的,寫(xiě)作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因?yàn)閷?xiě)作的時(shí)候發(fā)現(xiàn),為了弄懂一個(gè)知識(shí),不得不先去了解另外一些知識(shí),這樣以來(lái),為了說(shuō)明一個(gè)問(wèn)題,就要把一系列知識(shí)都了解一遍,寫(xiě)出來(lái)的文章就特別長(zhǎng)。 為了避免一篇...

    Yumenokanata 評(píng)論0 收藏0
  • Thread類(lèi)源碼解讀(3)——線(xiàn)程中斷interrupt

    摘要:現(xiàn)在終止一個(gè)線(xiàn)程,基本上只能靠曲線(xiàn)救國(guó)式的中斷來(lái)實(shí)現(xiàn)。中斷機(jī)制的核心在于中斷狀態(tài)和異常中斷狀態(tài)設(shè)置一個(gè)中斷狀態(tài)清除一個(gè)中斷狀態(tài)方法同時(shí)會(huì)返回線(xiàn)程原來(lái)的中斷的狀態(tài)。中斷異常中斷異常一般是線(xiàn)程被中斷后,在一些類(lèi)型的方法如中拋出。 前言 系列文章目錄 線(xiàn)程中斷是一個(gè)很重要的概念,通常,取消一個(gè)任務(wù)的執(zhí)行,最好的,同時(shí)也是最合理的方法,就是通過(guò)中斷。 本篇我們主要還是通過(guò)源碼分析來(lái)看看中斷的概...

    fevin 評(píng)論0 收藏0
  • 后臺(tái)開(kāi)發(fā)常問(wèn)面試題集錦(問(wèn)題搬運(yùn)工,附鏈接)

    摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線(xiàn)抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類(lèi)與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類(lèi)對(duì)象鎖和類(lèi)鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類(lèi)單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    spacewander 評(píng)論0 收藏0
  • 后臺(tái)開(kāi)發(fā)常問(wèn)面試題集錦(問(wèn)題搬運(yùn)工,附鏈接)

    摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線(xiàn)抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類(lèi)與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類(lèi)對(duì)象鎖和類(lèi)鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類(lèi)單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    xfee 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<