Thread類深度剖析-源碼學(xué)習(xí)

1 線程簡介

首先我們要清楚的是枢贿,任何程序想要實現(xiàn)線程都需要內(nèi)核線程的支持,至少要有一個楣黍,不然程序無法執(zhí)行斋配。用戶線程和內(nèi)核線程的比例(M:N)體現(xiàn)了線程的不同實現(xiàn)方式戏锹。而對于高版本的Java(1.3及以后版本)盗似,一個Java的Thread就對應(yīng)一個操作系統(tǒng)的線程俺亮。

/**
 * 可以創(chuàng)建多個線程
 * <p>
 * 每個線程都有優(yōu)先級绳矩。
 * 指優(yōu)先級越高罩润,越有可能先執(zhí)行。因為高優(yōu)先級的擁有更多資源翼馆,更容易搶到CPU資源而已割以。
 * 線程優(yōu)先級有繼承效果,比如main線程創(chuàng)建了線程A,則線程A初始優(yōu)先級繼承main的優(yōu)先級应媚,但是還可以修改為其他優(yōu)先級严沥。
 * <p>
 * 線程分為用戶線程和守護線程。
 * 用戶線程執(zhí)行完之后中姜,不管守護線程有沒有執(zhí)行完消玄,jvm都會關(guān)閉
 * 守護線程A創(chuàng)建的線程B也是守護線程跟伏,但是守護線程B在未執(zhí)行之前可以設(shè)置為非守護線程
 * setDemon()方法當(dāng)線程開始之后就不可以設(shè)置了
 * if (isAlive()) {
 * throw new IllegalThreadStateException();
 * }
 * <p>
 * 創(chuàng)建線程 1 :繼承Thread類
 * 創(chuàng)建線程 2 :實現(xiàn)Runnable接口
 * 創(chuàng)建線程 1 :實現(xiàn)Callable<>接口 通過FuntureTask(Callable<> caller) 實現(xiàn),
 * 本質(zhì)上還是第二種方式翩瓜,因為FutureTask實現(xiàn)了 Runnable接口
 * <p>
 * The following code would then create a thread and start it running:
 * <blockquote><pre>
 *     PrimeRun p = new PrimeRun(143);
 *     new Thread(p).start();
 * </pre></blockquote>
 * <p>
 * 每個線程的名稱都是不同的受扳,如果存在相同的線程名稱,會將設(shè)置相同名字的后一個線程重新生成名稱
 * <p>
 */
public class Thread implements Runnable {
/**
     * 初始化本地方法兔跌,(native方法) 當(dāng)執(zhí)行時不需要再去類庫加載勘高,提升性能 靜態(tài)方法,類級別的
     * 可以理解成為本類的本地方法 提前注冊進來 提升執(zhí)行時的性能
     * 都是靜態(tài)的坟桅,類裝載時執(zhí)行
     */
    private static native void registerNatives();

    static {
        registerNatives();
    }
}

2 線程上下文

CPU在一個時刻只能運行一個線程华望,當(dāng)在運行一個線程的過程中轉(zhuǎn)去運行另外一個線程,這個叫做線程上下文切換(對于進程也是類似)桦卒。

由于可能當(dāng)前線程的任務(wù)并沒有執(zhí)行完畢立美,所以在切換時需要保存線程的運行狀態(tài),以便下次重新切換回來時能夠繼續(xù)切換之前的狀態(tài)運行方灾。舉個栗子:線程A正在讀取文件建蹄,當(dāng)讀到一半時需要暫停線程A,轉(zhuǎn)去執(zhí)行線程B裕偿,當(dāng)再次切換回來執(zhí)行線程A的時候洞慎,我們不希望線程A又從文件的開頭來讀取。因此需要記錄線程A的運行狀態(tài)和執(zhí)行位置等嘿棘,那么會記錄哪些數(shù)據(jù)呢劲腿?因為下次恢復(fù)時需要知道在這之前當(dāng)前線程已經(jīng)執(zhí)行到哪條指令了,所以需要記錄程序計數(shù)器的值鸟妙,另外比如說線程正在進行某個計算的時候被掛起了焦人,那么下次繼續(xù)執(zhí)行的時候需要知道之前掛起時變量的值時多少,因此需要記錄CPU寄存器的狀態(tài)重父。所以一般來說花椭,線程上下文切換過程中會記錄程序計數(shù)器、CPU寄存器狀態(tài)等數(shù)據(jù)房午。

對于線程的上下文切換實際上就是 存儲和恢復(fù)CPU狀態(tài)的過程矿辽,它使得線程執(zhí)行能夠從中斷點恢復(fù)執(zhí)行。

雖然多線程可以使得任務(wù)執(zhí)行的效率得到提升郭厌,但是由于在線程切換時同樣會帶來一定的開銷代價袋倔,并且多個線程會導(dǎo)致系統(tǒng)資源占用的增加,所以在進行多線程編程時要注意這些因素折柠。

3 線程參數(shù)

private volatile String name; //線程名稱
private int priority; //線程優(yōu)先級
private Thread threadQ; //暫時不知道是干什么的宾娜,請教各位大佬 (感覺和JVM有關(guān))
private long eetop; //JVM中的JavaThread指針
private boolean single_step;//字面意思 是否單步執(zhí)行 請教各位大佬具體細節(jié)(感覺和JVM有關(guān))
private boolean daemon = false; //是否是守護線程,默認false
private boolean stillborn = false; //字面意思 夭折 請教各位大佬具體細節(jié)(感覺和JVM state有關(guān))
private Runnable target; //指定運行其中的Runnable扇售,一般都需要指定碳默,不指定的線程沒有意義贾陷,或者可以通過創(chuàng)建Thread的子類并重新run方法缘眶。
private ThreadGroup group; //指定當(dāng)前線程的線程組嘱根,未指定時線程組為創(chuàng)建該線程所屬的線程組。線程組可以用來管理一組線程巷懈,通過activeCount() 來查看活動線程的數(shù)量该抒。感覺除了這個沒有什么大的用處。如果有其他用處或理解歡迎留言
private ClassLoader contextClassLoader; //線程用的上下文類加載器顶燕,該上下文類加載器可供線程加載類和資源
private AccessControlContext inheritedAccessControlContext; //繼承的訪問系統(tǒng)資源的上下文(本線程的)
private static int threadInitNumber;//計數(shù)變量凑保,用在nextThreadNum方法中為匿名線程生成名稱
ThreadLocal.ThreadLocalMap threadLocals = null; //線程局部變量
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; //可繼承的線程局部變量 比如一些 logId uid token 什么項目中共用的一些參數(shù)及標(biāo)準 如果{true},則從構(gòu)造線程 繼承(inheritableThreadLocals) 的初始值 public的構(gòu)造器 此參數(shù)均為true
private long stackSize;// 新線程預(yù)期所需的堆棧大小涌攻,或者零表示該參數(shù)將被忽略欧引。與平臺相關(guān),不建議使用該屬性恳谎。
private long nativeParkEventPointer;//native線程終止后芝此,保持虛擬機私有狀態(tài)
private long tid;//線程id
private static long threadSeqNumber;//為了生成線程ID
private volatile int threadStatus = 0; // java線程狀態(tài),已初始化因痛,0代表'not yet started'
volatile Object parkBlocker; //LockSupport.park是提供給當(dāng)前調(diào)用的參數(shù),由 LockSupport.setBlocker 設(shè)置,通過LockSupport.getBlocker訪問
private volatile Interruptible blocker;//此線程在 可中斷I/O操作中 被阻塞的對象(如果有)婚苹。設(shè)置此線程的中斷狀態(tài)后,應(yīng)調(diào)用 該對象的中斷方法鸵膏。
private final Object blockerLock = new Object();

4 線程優(yōu)先級

1.java線程是通過映射到系統(tǒng)的原生線程上來實現(xiàn)的膊升,所以線程的調(diào)度最終還是取決于操作系統(tǒng),操作系統(tǒng)的優(yōu)先級與java的優(yōu)先級并不一一對應(yīng)谭企,如果操作系統(tǒng)的優(yōu)先級級數(shù)大于java的優(yōu)先級級數(shù)(10級)還好廓译,但是如果小于得的話就不行了,這樣會導(dǎo)致不同優(yōu)先級的線程的優(yōu)先級是一樣的债查。
2.優(yōu)先級可能會被系統(tǒng)自動改變非区,比如windows系統(tǒng)中就存在一個優(yōu)先級推進器,大致功能就是如果一個線程執(zhí)行的次數(shù)過多的話攀操,可能會越過優(yōu)先級為他分配執(zhí)行時間
3.線程的調(diào)度最終還是取決于操作系統(tǒng)中的線程規(guī)劃器院仿。其實,即使設(shè)置了線程的優(yōu)先級速和,一樣無法確保這個線程一定先執(zhí)行歹垫,因為它有很大的隨機性。它并無法控制執(zhí)行哪個線程颠放,因為線程的執(zhí)行排惨,是搶占資源后才能執(zhí)行的操作,而搶點資源時碰凶,最多是給于線程優(yōu)先級較高的線程一點機會而已暮芭,能不能抓住可是不一定的鹿驼。

說到底就一句話:線程優(yōu)化級較高的線程不一定先執(zhí)行。

5 線程狀態(tài)

線程狀態(tài)流轉(zhuǎn).png

線程內(nèi)部枚舉有6種狀態(tài):NEW辕宏、RUNNABLE畜晰、BLOCKED、WAITING瑞筐、TIMED_WAITING凄鼻、TERMINATED

NEW:新生;尚未啟動的線程的線程狀態(tài)

RUNNABLE:可運行聚假; 可運行線程的線程狀態(tài)块蚌。
處于可運行狀態(tài)的線程正在Java虛擬機中執(zhí)行,但可能需要等待其他資源調(diào)度膘格,如CPU峭范。

BLOCKED:阻塞;處于阻塞狀態(tài)的線程正在等待鎖瘪贱。BLOCKED 狀態(tài)可以視作是一種特殊的 WAITING纱控,特指等待鎖。

BLOCKED 源碼注釋:
1.一個處于 blocked 狀態(tài)的線程正在等待一個監(jiān)視器鎖以進入一個同步的塊或方法政敢。
A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or 
2.或在其調(diào)用 Object.wait 方法之后其徙,以再次進入一個同步的塊或方法。
reenter a synchronized block/method after calling Object.wait

第一句:本線程在等待其他線程釋放鎖喷户,獲得鎖之后才能執(zhí)行后面鎖住的代碼塊或者方法唾那。
第二句:本線程持有鎖,內(nèi)部調(diào)用 Object.wait 方法進入阻塞狀態(tài)褪尝,這時會釋放鎖闹获,等待其他線程 Object.notify方法將本線程喚醒,喚醒后需要重新獲取到鎖才能繼續(xù)執(zhí)行后面的代碼塊河哑”芊蹋可以看出以下幾點:
1.必須持有該鎖才能調(diào)用該鎖的 wait 方法,這是第一次 enter璃谨。
2.調(diào)用 wait 之后線程會釋放該鎖沙庐,并進入該鎖的等待隊列(wait set)中,狀態(tài)變?yōu)閃AITING佳吞。
3.當(dāng)收到其它線程對該鎖的 notify 或 notifyAll 通知之后拱雏,等待線程并不能立即恢復(fù)執(zhí)行,因為停止的地方是在同步塊內(nèi)底扳,而鎖已經(jīng)釋放了铸抑,所以它要重新獲取鎖才能再次執(zhí)行后續(xù)的同步塊reenter。這是第二次 enter衷模,所以叫 reenter鹊汛。
4.但是該線程還是要與其它線程去競爭鎖蒲赂,這一過程跟 enter 的過程一樣,因此也會因為鎖已經(jīng)被其它線程據(jù)有而導(dǎo)致 BLOCKED刁憋。

WAITING:等待滥嘴;一個正在無限期等待其他線程執(zhí)行完成處于這一狀態(tài),他和BLOCKED區(qū)別在于需不需要等待鎖职祷。

TIMED_WAITING:持續(xù)時間等待氏涩,有限期的等待;

TERMINATED:結(jié)束有梆。

6 線程方法

Thread Thread.currentThread() //獲得當(dāng)前線程的引用。
boolean isAlive() //測試線程是否處于活動狀態(tài)意系。
void setDaemon(boolean on) //設(shè)置線程是否為守護線程
boolean isDaemon() //測試線程是否為守護線程泥耀。
void checkAccess() //判斷當(dāng)前運行的線程是否有權(quán)修改該線程。
int Thread.activeCount()//當(dāng)前線程所在線程組中活動線程的數(shù)目蛔添。
StackTraceElement[] getStackTrace() //獲取堆棧數(shù)組痰催。
void dumpStack() //打印堆棧。
int enumerate(Thread[] tarray) //將當(dāng)前線程的線程組及其子組中的每一個活動線程復(fù)制到指定的數(shù)組中迎瞧。
Map<Thread,StackTraceElement[]> getAllStackTraces() //返回所有活動線程的堆棧跟蹤的一個映射夸溶。
boolean holdsLock(Object obj) //當(dāng)且僅當(dāng)當(dāng)前線程在指定的對象上保持監(jiān)視器鎖時,才返回 true凶硅。
void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) //設(shè)置由于未捕獲到異常導(dǎo)致線程終止時所調(diào)用的默認處理程序缝裁。
Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() //返回線程由于未捕獲到異常而突然終止時調(diào)用的默認處理程序。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() //返回該線程由于未捕獲到異常而突然終止時調(diào)用的處理程序足绅。
void interrupt() //中斷線程捷绑。
boolean isInterrupted()//測試線程是否已經(jīng)中斷。
boolean interrupted() //測試當(dāng)前線程是否已經(jīng)中斷氢妈。
void sleep(long millis) //休眠指定時間
void sleep(long millis, int nanos) //休眠指定時間
void yield() //釋放cpu資源進入就緒狀態(tài)重新爭搶資源粹污。意義不太大
void join() //等待該線程終止。(內(nèi)部也依賴的wait方法)
void join(long millis) //等待該線程終止的時間最長為 millis 毫秒首量。
void join(long millis, int nanos) //等待該線程終止的時間最長為 millis 毫秒 + nanos 納秒壮吩。
void setContextClassLoader(ClassLoader cl) //設(shè)置該線程的上下文 ClassLoader。
void start()//使該線程開始執(zhí)行加缘;Java 虛擬機調(diào)用該線程的 run 方法鸭叙。
void run() //線程啟動后執(zhí)行的方法。
比較重要的方法

/**
     * 使線程進入就緒狀態(tài)生百,等競爭到CPU資源后執(zhí)行下面的run()方法
     *
     * @throws IllegalThreadStateException 線程啟動后再次調(diào)用會報錯
     */
    public synchronized void start() {
        /**
         * VM創(chuàng)建的線程或者系統(tǒng)線程組創(chuàng)建的線程 不是通過此方法創(chuàng)建的. 該方法邏輯可能會變化.
         */
        //0是就緒狀態(tài) 已經(jīng)是就緒狀態(tài),再調(diào)用此方法后就會報錯
        if (threadStatus != 0) throw new IllegalThreadStateException();
        //通知線程組 該線程將要啟動
        group.add(this);
        boolean started = false;
        try {
            //native方法递雀,通過虛擬機啟動線程
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                //什么也不做。防止覆蓋start0拋出的異常蚀浆,讓該異常向上傳遞
            }
        }
    }
/**
     * 爭搶到CPU資源 線程真正執(zhí)行的方法
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

 /**
     * 中斷此線程.
     */
    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();
    }
/**
     * 測試當(dāng)前線程是否已被中斷缀程。并且清除線程的 中斷狀態(tài)
     * 當(dāng)前線程已被中斷 返回true 并且清除線程的 中斷狀態(tài)(置為false)
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
     * 測試當(dāng)前線程是否已被中斷搜吧。 和上面方法差別是該方法 不清除線程的中斷狀態(tài)
     * 當(dāng)前線程已被中斷 返回true
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    /**
     * private 測試當(dāng)前線程是否已被中斷 根據(jù)參數(shù)看是否需要清楚中斷狀態(tài)
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

Thread.sleep(1000); 與 Thread.currentThread().sleep(1000); 等價,都是在何處調(diào)用杨凑,就會使得當(dāng)前的線程進入休眠狀態(tài)滤奈, main線程調(diào)用threadB.sleep(1000)與調(diào)用Thread.sleep(1000)一樣,都是使main線程休眠1000毫秒撩满,而不是使threadB休眠蜒程。
sleep方法不會釋放鎖。因為不強制需要鎖
sleep()支持中斷伺帘,因此當(dāng)檢測到中斷信號(isInterrupt=1)時昭躺,會拋出InterruptedException 異常,因此在cache塊中可以對如果發(fā)生中斷進行某些操作伪嫁。通過isInterrupted()可以發(fā)現(xiàn):在拋出異常的時候领炫,還讓異常狀態(tài)重新置為了false(isInterrupt=0)。
需要注意:由于不允許不同的線程之間的異常溢出张咳,因此在run方法中就必須對此處拋出的InterruptedException異常進行處理帝洪,因此上述示例代碼,直接對異常進行了捕捉脚猾。若是在其他的普通方法中葱峡,除非是線程需要退出,否則都不應(yīng)當(dāng)直接將異常生吞龙助。此時可以選擇:1.不捕捉 InterruptedException砰奕,將它傳播給調(diào)用者;2.捕捉InterruptedException執(zhí)行必要的清理操作泌参,重新向上拋出InterruptedException異常脆淹;3.捕捉 InterruptedException 后重新恢復(fù)中斷狀態(tài)。

6 其他

創(chuàng)建線程:1Runnable接口沽一,2.繼承Thread類盖溺,實際上也是實現(xiàn)Runnable接口,3.Callable/Future 4.線程池

實際應(yīng)用:

1铣缠、后臺任務(wù)烘嘱,例如:文件跑批,文件導(dǎo)出蝗蛙,定時百萬用戶推送消息蝇庭;

2、異步處理捡硅,例如:多線程異步處理哮内,提高程序響應(yīng);記錄日志;Tomcat內(nèi)部分發(fā)請求北发;BIO模型優(yōu)化:
Socket socket = socket.accept();//阻塞纹因,連接阻塞
New Thread(new Handler(socket)).start();//解決了r/w阻塞問題

3、分布式計算

Thread類源碼

/**
 * 可以創(chuàng)建多個線程
 * <p>
 * 每個線程都有優(yōu)先級琳拨。
 * 指優(yōu)先級越高瞭恰,越有可能先執(zhí)行。因為高優(yōu)先級的擁有更多資源狱庇,更容易搶到CPU資源而已惊畏。
 * 線程優(yōu)先級有繼承效果,比如main線程創(chuàng)建了線程A,則線程A初始優(yōu)先級繼承main的優(yōu)先級密任,但是還可以修改為其他優(yōu)先級颜启。
 * <p>
 * 線程分為用戶線程和守護線程。
 * 用戶線程執(zhí)行完之后批什,不管守護線程有沒有執(zhí)行完农曲,jvm都會關(guān)閉
 * 守護線程A創(chuàng)建的線程B也是守護線程,但是守護線程B在未執(zhí)行之前可以設(shè)置為非守護線程
 * setDemon()方法當(dāng)線程開始之后就不可以設(shè)置了
 * if (isAlive()) {
 * throw new IllegalThreadStateException();
 * }
 * <p>
 * 創(chuàng)建線程 1 //繼承Thread類
 * 創(chuàng)建線程 2 //實現(xiàn)Runnable接口
 * 創(chuàng)建線程 1 //實現(xiàn)Callable<>接口 通過FuntureTask(Callable<> caller) 實現(xiàn)驻债,
 * 本質(zhì)上還是第二種方式,因為FutureTask實現(xiàn)了 Runnable接口
 * <p>
 * The following code would then create a thread and start it running:
 * <blockquote><pre>
 *     PrimeRun p = new PrimeRun(143);
 *     new Thread(p).start();
 * </pre></blockquote>
 * <p>
 * 每個線程的名稱都是不同的形葬,如果存在相同的線程名稱合呐,會將設(shè)置相同名字的后一個線程重新生成名稱
 * <p>
 */
public class Thread implements Runnable {
    /**
     * 初始化本地方法,(native方法) 當(dāng)執(zhí)行時不需要再去類庫加載笙以,提升性能 靜態(tài)方法淌实,類級別的
     * 可以理解成為本類的本地方法 提前注冊進來 提升執(zhí)行時的性能
     * 都是靜態(tài)的,類裝載時執(zhí)行
     */
    private static native void registerNatives();

    static {
        registerNatives();
    }

    private volatile String name; //線程名稱
    private int priority;         //線程優(yōu)先級
    private Thread threadQ;     //暫時不知道是干什么的猖腕,請教各位大佬 (感覺和JVM有關(guān))
    private long eetop; //JVM中的JavaThread指針
    private boolean single_step;//字面意思 是否單步執(zhí)行 請教各位大佬具體細節(jié)(感覺和JVM有關(guān))
    private boolean daemon = false; //是否是守護線程拆祈,默認false
    private boolean stillborn = false; //字面意思 夭折 請教各位大佬具體細節(jié)(感覺和JVM state有關(guān))
    /**
     * 指定運行其中的Runnable,一般都需要指定倘感,不指定的線程沒有意義放坏,或者可以通過創(chuàng)建Thread的子類并重新run方法。
     */
    private Runnable target;
    /**
     * 指定當(dāng)前線程的線程組老玛,未指定時線程組為創(chuàng)建該線程所屬的線程組淤年。
     * 線程組可以用來管理一組線程,通過activeCount() 來查看活動線程的數(shù)量蜡豹。感覺除了這個沒有什么大的用處麸粮。
     * 如果有其他用處或理解歡迎留言
     */
    private ThreadGroup group;
    /**
     * 線程用的上下文類加載器,該上下文類加載器可供線程加載類和資源
     */
    private ClassLoader contextClassLoader;
    /**
     * 繼承的訪問系統(tǒng)資源的上下文(本線程的)
     */
    private AccessControlContext inheritedAccessControlContext;
    /**
     * 計數(shù)變量镜廉,用在nextThreadNum方法中為匿名線程生成名稱
     */
    private static int threadInitNumber;

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

    /**
     * 線程局部變量
     */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    /**
     * 可繼承的線程局部變量 比如一些 logId uid token 什么項目中共用的一些參數(shù)及標(biāo)準
     * 如果{true}弄诲,則從構(gòu)造線程 繼承 (inheritableThreadLocals) 的初始值
     * public的構(gòu)造器 此參數(shù)均為true
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    /**
     * 新線程預(yù)期所需的堆棧大小,或者零表示該參數(shù)將被忽略娇唯。
     * 與平臺相關(guān)齐遵,不建議使用該屬性寂玲。
     */
    private long stackSize;
    private long nativeParkEventPointer;//native線程終止后,保持虛擬機私有狀態(tài)
    private long tid;//線程id
    private static long threadSeqNumber;//為了生成線程ID
    /**
     * java線程狀態(tài)洛搀,已初始化敢茁,0代表'not yet started'
     */
    private volatile int threadStatus = 0;

    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

    /**
     * java.util.concurrent.locks.LockSupport.park.是提供給當(dāng)前調(diào)用的參數(shù)
     * 由 java.util.concurrent.locks.LockSupport.setBlocker 設(shè)置
     * 通過  java.util.concurrent.locks.LockSupport.getBlocker訪問
     */
    volatile Object parkBlocker;

    /**
     * 此線程在 可中斷I/O操作中 被阻塞的對象(如果有)。
     * 設(shè)置此線程的中斷狀態(tài)后留美,應(yīng)調(diào)用 該對象的中斷方法彰檬。
     */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();

    /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
     */
    void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }

    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;

    /**
     * 返回 當(dāng)前正在執(zhí)行的線程
     */
    public static native Thread currentThread();

    /**
     * 向調(diào)度程序提示當(dāng)前線程愿意讓步,即讓出CPU資源和其他線程重新競爭資源
     * 感覺沒什么用(主動用的情況很少)
     */
    public static native void yield();

    /**
     * 使線程睡眠一段時間,線程掛起谎砾,讓出CPU資源
     * 不會釋放鎖逢倍,因為此操作不依賴加鎖 (有鎖的話是使用者自己主觀意愿,所以需要使用者自己操作釋放鎖)
     *
     * @throws InterruptedException 睡眠中的線程被中斷會拋出該異常景图,該異常會清除中斷位的狀態(tài)
     */
    public static native void sleep(long millis) throws InterruptedException;

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

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    /**
     * 使用當(dāng)前的AccessControlContext初始化線程较雕。
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) throw new NullPointerException("name cannot be null");
        this.name = name;
        Thread parent = currentThread();
        //獲取系統(tǒng)安全接口
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            //如果有系統(tǒng)安全manager,從manager中獲取當(dāng)前線程的線程組挚币。
            if (security != null) g = security.getThreadGroup();
            //如果線程組為空亮蒋,采用父類的線程組
            if (g == null) g = parent.getThreadGroup();
        }
        //校驗是否可以訪問
        g.checkAccess();
        //校驗是否允許
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        //增加線程組中未啟動線程的數(shù)量。
        // 未啟動的線程不會添加到線程組中妆毕,這樣即使從未啟動過的線程也不會被收集慎玖,
        // 但是必須對它們進行計數(shù),以便不會破壞其中未啟動的線程的守護程序線程組笛粘。
        g.addUnstarted();
        //賦值
        this.group = g;
        //初始守護狀態(tài)繼承父類 后面可以主動修改
        this.daemon = parent.isDaemon();
        //初始優(yōu)先級繼承父類 后面可以主動修改
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        //主動修改 優(yōu)先級
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            //true時繼承父類 共用參數(shù)
            this.inheritableThreadLocals =
                    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        this.stackSize = stackSize;
        //賦值tid
        tid = nextThreadID();
    }

    /**
     * 拋出CloneNotSupportedException趁怔,因為無法有意義地通過克隆構(gòu)造一個新的線程。
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

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

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

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

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

    /**
     * 使線程進入就緒狀態(tài)薪前,等競爭到CPU資源后執(zhí)行下面的run()方法
     *
     * @throws IllegalThreadStateException 線程啟動后再次調(diào)用會報錯
     */
    public synchronized void start() {
        /**
         * VM創(chuàng)建的線程或者系統(tǒng)線程組創(chuàng)建的線程 不是通過此方法創(chuàng)建的. 該方法邏輯可能會變化.
         */
        //0是就緒狀態(tài) 已經(jīng)是就緒狀態(tài),再調(diào)用此方法后就會報錯
        if (threadStatus != 0) throw new IllegalThreadStateException();
        //通知線程組 該線程將要啟動
        group.add(this);
        boolean started = false;
        try {
            //native方法润努,通過虛擬機啟動線程
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                //什么也不做。防止覆蓋start0拋出的異常示括,讓該異常向上傳遞
            }
        }
    }

    private native void start0();

    /**
     * 爭搶到CPU資源 線程真正執(zhí)行的方法
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

    /**
     * 系統(tǒng)調(diào)用此方法铺浇,使線程有機會在線程實際退出之前進行清理。
     */
    private void exit() {
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        target = null;
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

    /**
     * 強制退出 (類比 kill -9) 過期方法 已經(jīng)不建議使用
     */
    @Deprecated
    public final void stop() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            checkAccess();
            if (this != Thread.currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }
        // A zero status value corresponds to "NEW", it can't change to
        // not-NEW because we hold the lock.
        if (threadStatus != 0) {
            resume(); // Wake up thread if it was suspended; no-op otherwise
        }
        // The VM can handle all thread states
        stop0(new ThreadDeath());
    }

    @Deprecated
    public final synchronized void stop(Throwable obj) {
        throw new UnsupportedOperationException();
    }

    /**
     * 中斷此線程.
     */
    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();
    }

    /**
     * 測試當(dāng)前線程是否已被中斷例诀。并且清除線程的 中斷狀態(tài)
     * 當(dāng)前線程已被中斷 返回true 并且清除線程的 中斷狀態(tài)(置為false)
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
     * 測試當(dāng)前線程是否已被中斷随抠。 和上面方法差別是該方法 不清除線程的中斷狀態(tài)
     * 當(dāng)前線程已被中斷 返回true
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    /**
     * private 測試當(dāng)前線程是否已被中斷 根據(jù)參數(shù)看是否需要清楚中斷狀態(tài)
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

    @Deprecated
    public void destroy() {
        throw new NoSuchMethodError();
    }

    /**
     * 測試線程是否存活
     */
    public final native boolean isAlive();

    /**
     * 掛起這個線程
     */
    @Deprecated
    public final void suspend() {
        checkAccess();
        suspend0();
    }

    /**
     * 恢復(fù)掛起的線程
     */
    @Deprecated
    public final void resume() {
        checkAccess();
        resume0();
    }

    /**
     * 設(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)先級
     */
    public final int getPriority() {
        return priority;
    }

    public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }

    public final String getName() {
        return name;
    }

    public final ThreadGroup getThreadGroup() {
        return group;
    }

    /**
     * 返回線程組中活動線程數(shù)的估計值 是估計值的原因是線程調(diào)用了start之后就算在估計值中 并不一定正在執(zhí)行
     */
    public static int activeCount() {
        return currentThread().getThreadGroup().activeCount();
    }

    /**
     * 將當(dāng)前線程的線程組及其子組中的每個活動線程復(fù)制到指定的數(shù)組中
     */
    public static int enumerate(Thread tarray[]) {
        return currentThread().getThreadGroup().enumerate(tarray);
    }

    /**
     * 線程必須被掛起 然后計算此線程中的堆棧幀數(shù)并返回。
     */
    @Deprecated
    public native int countStackFrames();

    /**
     * 最多等待多少時間 使該線程結(jié)束
     */
    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 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);
    }

    /**
     * 等待線程執(zhí)行完
     */
    public final void join() throws InterruptedException {
        join(0);
    }

    public static void dumpStack() {
        new Exception("Stack trace").printStackTrace();
    }

    public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

    public final boolean isDaemon() {
        return daemon;
    }

    public final void checkAccess() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkAccess(this);
        }
    }

    public String toString() {
        ThreadGroup group = getThreadGroup();
        if (group != null) {
            return "Thread[" + getName() + "," + getPriority() + "," +
                    group.getName() + "]";
        } else {
            return "Thread[" + getName() + "," + getPriority() + "," +
                    "" + "]";
        }
    }

    @CallerSensitive
    public ClassLoader getContextClassLoader() {
        if (contextClassLoader == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader.checkClassLoaderPermission(contextClassLoader,
                    Reflection.getCallerClass());
        }
        return contextClassLoader;
    }

    public void setContextClassLoader(ClassLoader cl) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
        }
        contextClassLoader = cl;
    }

    /**
     * 當(dāng)且僅當(dāng) 當(dāng)前線程 持有入?yún)㈡i時繁涂,才返回 true
     */
    public static native boolean holdsLock(Object obj);

    private static final StackTraceElement[] EMPTY_STACK_TRACE
            = new StackTraceElement[0];

    public StackTraceElement[] getStackTrace() {
        if (this != Thread.currentThread()) {
            // check for getStackTrace permission
            SecurityManager security = System.getSecurityManager();
            if (security != null) {
                security.checkPermission(
                        SecurityConstants.GET_STACK_TRACE_PERMISSION);
            }
            // optimization so we do not call into the vm for threads that
            // have not yet started or have terminated
            if (!isAlive()) {
                return EMPTY_STACK_TRACE;
            }
            StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[]{this});
            StackTraceElement[] stackTrace = stackTraceArray[0];
            // a thread that was alive during the previous isAlive call may have
            // since terminated, therefore not having a stacktrace.
            if (stackTrace == null) {
                stackTrace = EMPTY_STACK_TRACE;
            }
            return stackTrace;
        } else {
            // Don't need JVM help for current thread
            return (new Exception()).getStackTrace();
        }
    }

    public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
        // check for getStackTrace permission
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkPermission(
                    SecurityConstants.GET_STACK_TRACE_PERMISSION);
            security.checkPermission(
                    SecurityConstants.MODIFY_THREADGROUP_PERMISSION);
        }

        // Get a snapshot of the list of all threads
        Thread[] threads = getThreads();
        StackTraceElement[][] traces = dumpThreads(threads);
        Map<Thread, StackTraceElement[]> m = new HashMap<>(threads.length);
        for (int i = 0; i < threads.length; i++) {
            StackTraceElement[] stackTrace = traces[i];
            if (stackTrace != null) {
                m.put(threads[i], stackTrace);
            }
            // else terminated so we don't put it in the map
        }
        return m;
    }

    private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
            new RuntimePermission("enableContextClassLoaderOverride");

    private static class Caches {
        static final ConcurrentMap<WeakClassKey, Boolean> subclassAudits =
                new ConcurrentHashMap<>();

        static final ReferenceQueue<Class<?>> subclassAuditsQueue =
                new ReferenceQueue<>();
    }

    private static boolean isCCLOverridden(Class<?> cl) {
        if (cl == Thread.class)
            return false;

        processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
        WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
        Boolean result = Caches.subclassAudits.get(key);
        if (result == null) {
            result = Boolean.valueOf(auditSubclass(cl));
            Caches.subclassAudits.putIfAbsent(key, result);
        }

        return result.booleanValue();
    }

    private static boolean auditSubclass(final Class<?> subcl) {
        Boolean result = AccessController.doPrivileged(
                new PrivilegedAction<Boolean>() {
                    public Boolean run() {
                        for (Class<?> cl = subcl;
                             cl != Thread.class;
                             cl = cl.getSuperclass()) {
                            try {
                                cl.getDeclaredMethod("getContextClassLoader", new Class<?>[0]);
                                return Boolean.TRUE;
                            } catch (NoSuchMethodException ex) {
                            }
                            try {
                                Class<?>[] params = {ClassLoader.class};
                                cl.getDeclaredMethod("setContextClassLoader", params);
                                return Boolean.TRUE;
                            } catch (NoSuchMethodException ex) {
                            }
                        }
                        return Boolean.FALSE;
                    }
                }
        );
        return result.booleanValue();
    }

    private native static StackTraceElement[][] dumpThreads(Thread[] threads);

    private native static Thread[] getThreads();

    public long getId() {
        return tid;
    }

    /**
     * NEW:A thread that has not yet started is in this state.
     * RUNNABLE:A thread executing in the Java virtual machine is in this state.
     * BLOCKED:A thread that is blocked waiting for a monitor lock is in this state.
     * WAITING:A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
     * TIMED_WAITING:A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
     * TERMINATED:A thread that has exited is in this state.
     */
    public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

    public State getState() {
        // get current thread state
        return sun.misc.VM.toThreadState(threadStatus);
    }

    @FunctionalInterface
    public interface UncaughtExceptionHandler {
        /**
         * Method invoked when the given thread terminates due to the
         * given uncaught exception.
         * <p>Any exception thrown by this method will be ignored by the
         * Java Virtual Machine.
         *
         * @param t the thread
         * @param e the exception
         */
        void uncaughtException(Thread t, Throwable e);
    }

    private volatile UncaughtExceptionHandler uncaughtExceptionHandler;

    private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(
                    new RuntimePermission("setDefaultUncaughtExceptionHandler")
            );
        }

        defaultUncaughtExceptionHandler = eh;
    }

    public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
        return defaultUncaughtExceptionHandler;
    }

    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
                uncaughtExceptionHandler : group;
    }

    public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        checkAccess();
        uncaughtExceptionHandler = eh;
    }

    private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

    static void processQueue(ReferenceQueue<Class<?>> queue,
                             ConcurrentMap<? extends
                                     WeakReference<Class<?>>, ?> map) {
        Reference<? extends Class<?>> ref;
        while ((ref = queue.poll()) != null) {
            map.remove(ref);
        }
    }

    static class WeakClassKey extends WeakReference<Class<?>> {
        /**
         * saved value of the referent's identity hash code, to maintain
         * a consistent hash code after the referent has been cleared
         */
        private final int hash;

        /**
         * Create a new WeakClassKey to the given object, registered
         * with a queue.
         */
        WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
            super(cl, refQueue);
            hash = System.identityHashCode(cl);
        }

        /**
         * Returns the identity hash code of the original referent.
         */
        @Override
        public int hashCode() {
            return hash;
        }

        /**
         * Returns true if the given object is this identical
         * WeakClassKey instance, or, if this object's referent has not
         * been cleared, if the given object is another WeakClassKey
         * instance with the identical non-null referent as this one.
         */
        @Override
        public boolean equals(Object obj) {
            if (obj == this)
                return true;

            if (obj instanceof WeakClassKey) {
                Object referent = get();
                return (referent != null) &&
                        (referent == ((WeakClassKey) obj).get());
            } else {
                return false;
            }
        }
    }


    @sun.misc.Contended("tlr")
    long threadLocalRandomSeed;

    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

    @sun.misc.Contended("tlr")
    int threadLocalRandomSecondarySeed;

    private native void setPriority0(int newPriority);

    private native void stop0(Object o);

    private native void suspend0();

    private native void resume0();

    private native void interrupt0();

    private native void setNativeName(String name);
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拱她,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扔罪,更是在濱河造成了極大的恐慌秉沼,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異唬复,居然都是意外死亡矗积,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門敞咧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棘捣,“玉大人,你說我怎么就攤上這事休建≌Э郑” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵测砂,是天一觀的道長茵烈。 經(jīng)常有香客問我,道長砌些,這世上最難降的妖魔是什么呜投? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮存璃,結(jié)果婚禮上仑荐,老公的妹妹穿的比我還像新娘。我一直安慰自己纵东,他們只是感情好释漆,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著篮迎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪示姿。 梳的紋絲不亂的頭發(fā)上甜橱,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音栈戳,去河邊找鬼岂傲。 笑死,一個胖子當(dāng)著我的面吹牛子檀,可吹牛的內(nèi)容都是我干的镊掖。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼褂痰,長吁一口氣:“原來是場噩夢啊……” “哼亩进!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缩歪,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤归薛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體主籍,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡习贫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了千元。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苫昌。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖幸海,靈堂內(nèi)的尸體忽然破棺而出祟身,到底是詐尸還是另有隱情,我是刑警寧澤涕烧,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布月而,位于F島的核電站,受9級特大地震影響议纯,放射性物質(zhì)發(fā)生泄漏父款。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一瞻凤、第九天 我趴在偏房一處隱蔽的房頂上張望憨攒。 院中可真熱鬧,春花似錦阀参、人聲如沸肝集。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杏瞻。三九已至,卻和暖如春衙荐,著一層夾襖步出監(jiān)牢的瞬間捞挥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工忧吟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留砌函,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓溜族,卻偏偏與公主長得像讹俊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子煌抒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351