java源碼賞析--java.lang.Thread

1. 線程狀態(tài)

 public enum State {
        
    NEW, RUNNABLE, BLOCKED, WAITING,  TIMED_WAITING, TERMINATED;
}

說明:

狀態(tài) 描述
NEW 至今尚未啟動的線程的狀態(tài)
RUNNABLE 可運行線程的線程狀態(tài)
BLOCKED 受阻塞并且正在等待監(jiān)視器鎖的某一線程的線程狀態(tài)
WAITING 某一等待線程的線程狀態(tài)
TIMED_WAITING 具有指定等待時間的某一等待線程的線程狀態(tài)
TERMINATED 已終止線程的線程狀態(tài)。線程已經(jīng)結(jié)束執(zhí)行

如圖:

2. 主要屬性


/*
 * 線程名字,通過構(gòu)造參數(shù)來指定  
 */
private volatile String name;

/*
 * 表示線程的優(yōu)先級,優(yōu)先級越高,越優(yōu)先被執(zhí)行(最大值為10,最小值為1,默認值為5)
 */
private int  priority;

/*
 * 線程是否是守護線程:當所有非守護進程結(jié)束或死亡后,程序?qū)⑼V? */   
private boolean  daemon = false;

/*
 * 將要執(zhí)行的任務(wù)
 */  
private Runnable target;

/*
 * 線程組表示一個線程的集合。此外,線程組也可以包含其他線程組。線程組構(gòu)成一棵樹,在樹中,除了初始線程組外,每個線程組都有一個父線程組。
 */ 
private ThreadGroup group;

/*
 * Thread ID
 */ 
private long tid;

/*
 * 用來生成Thread ID使用
 */ 
private static long threadSeqNumber;

/*
 * 第幾個線程,在init初始化線程的時候用來賦給thread.name
 */ 
private static int threadInitNumber


/*
 * 線程從創(chuàng)建到最終的消亡,要經(jīng)歷若干個狀態(tài)。 
 * 一般來說,線程包括以下這幾個狀態(tài):創(chuàng)建(new)、就緒(runnable)、運行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)
 */
private volatile int threadStatus = 0;

3. 構(gòu)造函數(shù)

源碼

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);
}

public Thread(String name) {
    init(null, null, name, 0);
}

public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);
}

public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}

public Thread(ThreadGroup group, Runnable target, String name) {
    init(group, target, name, 0);
}

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

構(gòu)造函數(shù)中第三個參數(shù)為線程名稱,而"Thread-" + nextThreadNum()是系統(tǒng)默認的線程名,會將屬性中的threadInitNumber加一。

private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

4. 啟動線程

4.1 start()方法

源碼

public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0){// 如果狀態(tài)不是`NEW`
        throw new IllegalThreadStateException();
    }
    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

4.2 run方法

源碼

public void run() {
    if (target != null) {
        target.run();
    }
}

5. 守護線程與非守護線程(用戶線程)

守護線程與普通線程的唯一區(qū)別是:

  • 任何一個守護線程都是整個JVM中所有非守護線程的保姆

  • 守護線程與main同生共死,當main退出,它將終止,而普通線程是在任務(wù)執(zhí)行結(jié)束才停止。

  • Java虛擬機在它所有非守護線程已經(jīng)離開后自動離開。守護線程則是用來服務(wù)用戶線程的,如果沒有其他用戶線程在運行,那么就沒有可服務(wù)對象,也就沒有理由繼續(xù)下去。

示例

public class ThreadDemo extends Thread {

    public void run() {
        while (true) {
            for (int i = 1; i <= 100; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread daemonThread = new Thread(new ThreadDemo());
        daemonThread.setName("測試thread");
        // 設(shè)置為守護進程
        daemonThread.setDaemon(true);
        daemonThread.start();

        System.out.println("isDaemon = " + daemonThread.isDaemon());
        Thread t = new Thread(new ThreadDemo());// 為非守護線程
        t.start();
    }
}

因為有線程t 的存在,守護線程daemonThread 一直執(zhí)行。

public static void main(String[] args) {
    Thread daemonThread = new Thread(new ThreadDemo());
    daemonThread.setName("測試thread");
    // 設(shè)置為守護進程
    daemonThread.setDaemon(true);
    daemonThread.start();
}

如果只有守護線程daemonThread,程序因為main線程的退出而退出。

注意點:

  • thread.setDaemon(true)必須在thread.start()之前設(shè)置,否則會跑出一個IllegalThreadStateException異常。你不能把正在運行的常規(guī)線程設(shè)置為守護線程。

  • 在Daemon線程中產(chǎn)生的新線程也是Daemon的

  • 不要認為所有的應(yīng)用都可以分配給Daemon來進行服務(wù),比如讀寫操作或者計算邏輯

6. 主要方法

6.1 sleep(long millis)

源碼

public static native void sleep(long millis) throws InterruptedException;

注意點:

  • sleep是指線程被調(diào)用時,占著CPU不工作,形象地說明為“占著CPU睡覺”,此時,系統(tǒng)的CPU部分資源被占用,其他線程無法進入。

  • sleep方法不會釋放鎖。

示例:

public class ThreadDemo extends Thread {

    private static Long i = 0L;

    public void run() {
        while (true) {
            synchronized (ThreadDemo.class) {
                i++;
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new ThreadDemo();
        t.start();

        Thread t1 = new ThreadDemo();
        t1.start();
    }
}

兩個線程(A、B)同時執(zhí)行,當線程A獲得同步鎖并進入同步塊時,線程B會處于BLOCKED狀態(tài)等待鎖。而當線程A執(zhí)行sleep后,線程A狀態(tài)為TIMED_WAITING,而由于sleep不會釋放鎖,所以線程B的狀態(tài)不會改變。

6.2 yield()

源碼

public static native void yield();

注意點:

  • 調(diào)用yield方法會讓當前線程交出CPU權(quán)限,讓擁有相同優(yōu)先級的線程有獲取CPU執(zhí)行時間的機會

  • 調(diào)用yield方法并不會讓線程進入BLOCKED狀態(tài),而是讓線程重回RUNNABLE狀態(tài), 但不能保證迅速轉(zhuǎn)換。

  • 不會釋放鎖

6.3 isAlive()

表示線程當前是否為可用狀態(tài),如果線程已經(jīng)啟動,并且沒有死亡,則返回true,否則為false

源碼

public final native boolean isAlive();

6.4 join()

在很多情況下,主線程生成并起動了子線程,如果子線程里要進行大量的耗時的運算,主線程往往將于子線程之前結(jié)束,但是如果主線程處理完其他的事務(wù)后,需要用到子線程的處理結(jié)果,也就是主線程需要等待子線程執(zhí)行完成之后再結(jié)束,這個時候就要用到j(luò)oin()方法

源碼

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;
        }
    }
}

public final void join() throws InterruptedException {
    join(0);
}

注意點:

  • join方法是通過wait方法實現(xiàn)的,所以會釋放對象鎖。

  • 調(diào)用方線程狀態(tài)為WAITING。

  • join需要在start之后調(diào)用

示例一

public class ThreadDemo extends Thread {

    private static Long i = 0L;

    public void run() {
        synchronized (this) {
            while (true) {
                i++;
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new ThreadDemo();
        t.start();
        Thread.sleep(1000);
        t.join();
    }
}

此處需要注意的是,此處t.join()不會被執(zhí)行,main線程會一直等待對象鎖而BLOCKED

6.5 interrupt()

Thread.sleep、Thread.join、Object.wait等在檢查到線程的中斷狀態(tài)時,會拋出InterruptedException,同時會清除線程的中斷狀態(tài).

在java的線程Thread類中有三個方法,比較容易混淆,在這里解釋一下:

  • interrupt:設(shè)置線程的中斷狀態(tài)

  • isInterrupt:線程是否中斷,不會清楚中斷狀態(tài)。

  • interrupted:返回線程的上次的中斷狀態(tài),并清除中斷狀態(tài)。

示例

public class ThreadDemo extends Thread {

    public void run() {
        System.out.println("即將睡眠");
        try {
            Thread.sleep(1000 * 4);
        } catch (InterruptedException e) {
            System.out.println("被喚醒");
        }
        System.out.println("處理其他事情");
    }

    public static void main(String[] args) throws Exception {
        Thread t = new ThreadDemo();
        t.start();
        Thread.sleep(1000);
        t.interrupt();
    }
}

注意點:

  • Thread.interrupt()方法不會中斷一個正在運行的線程

  • 如果線程在調(diào)用 Object 類的 wait()、wait(long)wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long)sleep(long, int) 方法過程中受阻,則其中斷狀態(tài)將被清除,它還將收到一個InterruptedException異常。

  • synchronized在獲鎖的過程中是不能被中斷的,意思是說如果產(chǎn)生了死鎖,則不可能被中斷

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容