java synchronized原理

java synchronized原理

思考

  • 當synchronized加的是偏向鎖或者輕量級鎖的時候掂榔,調(diào)用 wait方法會怎樣
    • 對象的wait方法要依賴Monitor對象的實現(xiàn)败匹,而且需要有個隊列來存儲阻塞等待的線程是钥,偏向鎖和輕量級鎖都不涉及線程的阻塞,所以肖爵,我猜測會進行鎖膨脹為重量級鎖卢鹦,然后調(diào)用Monitor對象的wait方法
  • 為什么重量級鎖叫鎖膨脹
    • 重量級鎖會將對象頭Mark World指向一個Monitor對象,Monitor對象更像是對象頭的補充劝堪,在該對象中存儲了持有鎖的線程ID冀自、阻塞線程隊列、等待線程隊列秒啦,當這倆隊列中有信息時熬粗,這個Monitor對象就得一直掛在對象頭上。就像是對對象要實現(xiàn)鎖功能的補充余境,所以叫鎖膨脹
  • 假如一個重入3次的線程調(diào)用wait方法驻呐,怎么處理
    • 當前線程阻塞,并加入到Monitor的等待隊列猜拾,節(jié)點Node至少要有兩個信息挎袜,一個線程ID宋雏,一個重入次數(shù)

知識導讀

  • synchronized方法依賴標記flag為ACC_SYNCHRONIZED,同步代碼塊依賴monitorenter和monitorexit指令
  • synchronized在獲鎖的過程中是不能被中斷的笼沥,意思是說如果產(chǎn)生了死鎖奔浅,則不可能被中斷
  • 同步方法汹桦、同步代碼塊中拋出異常舞骆,鎖自動釋放
  • synchronized是可重入的非公平鎖實現(xiàn)
  • 無鎖督禽、偏向鎖总处、輕量級鎖不會出現(xiàn)線程阻塞的情況鹦马,這也是JVM優(yōu)化的最重要的一個目的
  • 無鎖荸频、偏向鎖、輕量級鎖蔑滓、自旋鎖键袱、重量級鎖的優(yōu)化場景及鎖升級過程
  • 鎖對象的方法實現(xiàn)依賴Monitor對象的實現(xiàn)褐健,Monitor對象依賴操作系統(tǒng)的Mutex互斥鎖實現(xiàn)
  • Monitor對象可以類比AQS蚜迅,封裝了持有鎖的線程,被阻塞線程隊列刹帕、等待線程隊列谎替、重入次數(shù)等信息
  • java的線程是映射到操作系統(tǒng)的原生內(nèi)核線程之上的钱贯,如果要阻塞或喚醒一條線程挫掏,則需要操作系統(tǒng)來幫忙完成秩命,這就不可避免地陷入用戶態(tài)到核心態(tài)的轉(zhuǎn)換中,進行這種狀態(tài)轉(zhuǎn)換需要耗費很多的處理器時間硫麻。
  • 計算過鎖對象的hashCode之后,對象頭上存儲了hashCode值拿愧,該鎖對象永遠無法作為偏向鎖了杠河,每次加鎖會直接走輕量級鎖或重量級鎖流程

對象鎖

Java 中的每一個對象都可以作為鎖

  • 對于同步方法浇辜,鎖是當前實例對象柳洋。
  • 對于同步方法塊卑雁,鎖是 Synchonized 括號里配置的對象。
  • 對于靜態(tài)同步方法莹捡,鎖是當前對象的 Class 對象篮赢。

對象在內(nèi)存的分布分為3個部分:對象頭,實例數(shù)據(jù)琉挖,和對齊填充启泣。在對象頭中的Mark Word存儲了對象的鎖信息。Java中鎖對象有四種狀態(tài)示辈,無鎖狀態(tài)寥茫,偏向鎖狀態(tài),輕量級鎖狀態(tài)和重量級鎖狀態(tài)顽耳,它會隨著競爭情況逐漸升級坠敷。鎖可以升級但不能降級,目的是為了提高獲得鎖和釋放鎖的效率

image

注意:在偏向鎖射富、輕量級鎖、重量級鎖狀態(tài)下粥帚,對象頭的hashCode位置被鎖標志占用

  • 輕量級鎖狀態(tài)下胰耗,HashCode會被復制到棧空間的Lock Record中
  • 重量級鎖狀態(tài)下芒涡,HashCode會被存儲在對象頭關(guān)聯(lián)的Monitor對象中
  • 偏向鎖狀態(tài)下沒有地方存儲HashCode柴灯,所以一旦遇到計算對象的hashCode,偏向鎖會升級到重量級鎖费尽。對象的hashCode是延遲計算的赠群,當一個對象已經(jīng)計算過hashCode,對象頭存儲了hashCode值旱幼,那么該對象鎖永遠無法作為偏向鎖使用了

原理

synchronized方法

javap查看synchronized方法會被編譯成方法的flags加上標志ACC_SYNCHRONIZED.

線程執(zhí)行方法時會檢查方法的ACC_SYNCHRONIZED標志查描,如果設置了線程需要先去獲取對象鎖,執(zhí)行完畢后線程再釋放對象鎖柏卤,在方法執(zhí)行期間冬三,同一時刻只有一個線程能成功獲取鎖

public synchronized void test(){
System.out.println("test");
}

public synchronized void test();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
       0: return

synchronized代碼塊

javap查看同步塊的入口位置和退出位置分別插入monitorenter和monitorexit字節(jié)碼指令。原理同synchronized方法一致

public void test(){
  synchronized (this) {
    
  }
}
public void test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_1
         5: monitorexit
         6: goto          14
         9: astore_2
        10: aload_1
        11: monitorexit
        12: aload_2
        13: athrow
        14: return
          

內(nèi)存語義

  • 當線程獲取鎖時缘缚,JMM會把該線程對應的本地內(nèi)存置為無效勾笆,同步代碼塊必須去主內(nèi)存讀取所需的共享變量。
  • 當線程釋放鎖時桥滨,JMM會將當前線程工作內(nèi)存中的共享變量刷新到主內(nèi)存中

優(yōu)化

synchronized重量級鎖依賴操作系統(tǒng)的mutex互斥鎖實現(xiàn)窝爪,需要阻塞線程弛车,由于線程的阻塞和重啟涉及CPU內(nèi)核切換,非常耗費性能蒲每,Jdk1.6之后針對synchronized做了一些優(yōu)化纷跛,來降低線程阻塞的幾率,主要包括如鎖粗化啃勉、鎖消除忽舟、輕量級鎖、偏向鎖淮阐、適應性自旋叮阅、重量級鎖等技術(shù)來減少鎖操作的開銷。

image
image

無鎖

無鎖沒有對資源進行鎖定泣特,所有的線程都能訪問并修改同一個資源,但同時只有一個線程能修改成功状您。

無鎖的實現(xiàn)主要依賴CAS操作勒叠,一般在一個循環(huán)中不斷的進行CAS操作直到成功。循環(huán)次數(shù)過多會導致CPU負載升高

偏向鎖

當鎖對象第一次被線程獲取的時候,虛擬機將會把對象頭中的標志位設置為“01”柒桑、把偏向模式設置為“1”魁淳,表示進入偏向模式界逛。同時使用CAS操作把獲取到這個鎖的線程ID記錄在Mark Word之中息拜。如果CAS操作成功该溯,持有偏向鎖的線程以后每次獲取該鎖時狈茉,虛擬機都可以不再進行任何同步操作

優(yōu)化背景

偏向鎖的目的是消除無競爭情況下的加鎖操作氯庆。大多數(shù)情況下鎖不存在競爭羽莺,總是由同一個線程操作鎖盐固。如果在接下來的執(zhí)行過程中,該鎖沒有被其他的線程獲取,則持有偏向鎖的線程將永遠不需要再進行同步和CAS

加鎖

  1. 檢查鎖對象頭的Mark Word是否為鎖狀態(tài)是否是01,即無鎖或者偏向鎖
    1. 如果鎖狀態(tài)不是01,進入輕量級鎖或者重量級鎖加鎖流程
    2. 如果Mark Word存儲了hashCode派撕,不可偏向?qū)ο螅苯舆M入輕量級鎖加鎖流程
    3. 如果是01际跪,可偏向鎖姆打,檢查Mark Word儲存的偏向線程ID是否為當前線程ID
      1. 如果存儲的偏向線程ID為空闲延,CAS將Mark Word偏向線程ID設置為本線程陆馁,CAS成功設置偏向鎖標志為1,獲取偏向鎖成功
      2. 如果是當前線程ID,偏向線程為當前線程互婿,獲取偏向鎖成功
      3. 如果對象頭存儲的偏向線程ID不是當前線程ID壮锻,進入偏向鎖撤銷流程

釋放鎖

偏向鎖使用了一種等到競爭出現(xiàn)才釋放鎖的機制,持有偏向鎖的線程不會主動釋放偏向鎖奏夫,需要等待其他線程來競爭的時候才會釋放偏向鎖呛哟。當有線程競爭偏向鎖時者娱,進入以下流程

  1. 當?shù)竭_全局安全點(在這個時間點上沒有字節(jié)碼正在執(zhí)行)時掛起持有偏向鎖的線程
    1. 當前線程檢查持有偏向鎖的線程是否還存活
      1. 偏向鎖線程不處于活動狀態(tài)增炭,將對象頭設置為無鎖狀態(tài),清除Mark Word中的偏向ID奈嘿,當前線程重新進入獲取偏向鎖流程
      2. 偏向鎖線程處于活動狀態(tài)袄膏,判斷當前線程是否還在同步代碼塊中占有鎖
        1. 偏向線程出了同步代碼塊码党,不競爭鎖了蒸健,將對象頭設置為無鎖狀態(tài)蘑秽,清除Mark Word中的偏向ID肠牲,當前線程重新進入獲取偏向鎖流程
        2. 偏向線程還持有鎖幼衰,發(fā)生競爭,升級為輕量級鎖
          1. Mark Word指向擁有偏向鎖線程的椬忽ǎ空間**的lock record
          2. 將對象頭鎖狀態(tài)修改為00,升級為輕量級鎖
          3. 恢復被掛起的偏向線程渡嚣,持有偏向鎖的線程獲取執(zhí)行權(quán),當前線程CAS自旋獲取輕量級鎖

注意:在競爭激烈的時候,偏向鎖會成為一種累贅识椰,要頻繁的暫停線程绝葡,可以通過設置 -XX:UseBiasedLocking=false關(guān)閉偏向鎖功能

輕量級鎖

在當前線程的棧幀中開辟一塊鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的Mark Word的拷貝腹鹉,然后CAS嘗試將對象的Mark Word指向Lock Record藏畅,就表示持有輕量級鎖

優(yōu)化背景

大部分占用鎖時間不會太長,通過短暫的自旋后可以獲取到鎖种蘸,避免線程阻塞和喚醒墓赴。輕量級鎖通過簡單的將對象頭指向持有鎖的棧來標記加鎖成,當發(fā)生競爭時航瞭,先自旋CAS更新Mark World指向本線程來競爭鎖诫硕。

加鎖

  1. 判斷鎖對象Mark Word中存儲的鎖記錄是否指向當前線程棧幀
    1. 如果指向當前線程棧幀,說明是重入鎖刊侯,當前線程直接獲取執(zhí)行權(quán)
    2. 如果沒有指向當前線程棧幀章办,說明其他線程已經(jīng)獲取了輕量級鎖。在當前線程棧幀中開辟鎖記錄空間滨彻,用于存放鎖對象中的Mark Word的拷貝(Displaced Mark Word),CAS將鎖對象的Mark Word指向當前椗航欤空間的鎖記錄
      1. CAS更新成功,成功獲取輕量級鎖亭饵,將Mark Word的鎖標志置為00休偶,執(zhí)行同步代碼塊
      2. CAS更新失敗,進入自旋鎖流程辜羊,當前線程嘗試使用自旋來獲取鎖踏兜,直到獲取到鎖或者升級為重量級鎖后阻塞當前線程

釋放鎖

  1. 從當前線程的棧幀中取出Displaced Mark Word存儲的鎖記錄的內(nèi)容
  2. 當前線程嘗試使用CAS將鎖記錄中復制的Displaced Mark Word替換到鎖對象中的Mark Word
    1. CAS更新成功,則釋放鎖成功八秃,釋放輕量級鎖碱妆,將鎖標志位置為01無鎖狀態(tài)
    2. CAS更新失敗,對象頭已經(jīng)由其他競爭的線程修改為10昔驱,Mark World已經(jīng)膨脹指向Monitor對象疹尾,當前鎖升級為重量級鎖,所以當前線程需要執(zhí)行重量級鎖釋放鎖流程骤肛,請看下文

自旋鎖

線程的阻塞和喚醒需要從用戶態(tài)轉(zhuǎn)換到核心態(tài)纳本,這個狀態(tài)之間的轉(zhuǎn)換需要相對比較長的時間,時間成本相對較高腋颠,自旋鎖就是避免線程進入阻塞狀態(tài)饮醇。在大多數(shù)情況下,線程持有鎖的時間都不會太長秕豫,所以希望通過短時間的重復嘗試去獲取鎖,避免線程阻塞。

當線程在獲取輕量級鎖的過程中執(zhí)行CAS更新Mark World失敗時,為了避免線程真實地在操作系統(tǒng)層面掛起,虛擬機通過自旋不斷嘗試CAS更新Mark World契耿。

  1. 自旋若干次后吹散,CAS修改Mark World成功則成功獲取輕量級鎖,線程獲取執(zhí)行權(quán)告喊。
  2. 自旋若干次后還是獲取鎖失敗,當前線程進入阻塞狀態(tài),升級為重量級鎖

注意:自旋等待雖然避免了線程切換的開銷狗准,但它要占用處理器時間。如果鎖被占用的時間很短茵肃,自旋等待的效果就會非常好腔长。反之,如果鎖被占用的時間很長验残,那么自旋的線程只會白浪費處理器資源捞附。所以,自旋等待的時間必須要有一定的限度您没,如果自旋超過了限定次數(shù)(默認是10次鸟召,可以使用-XX:PreBlockSpin來更改)沒有成功獲得鎖,就應當掛起線程氨鹏。

自適應自旋鎖

自旋是需要消耗CPU性能的欧募,為了避免競爭激烈情況下無意義的自旋,JDK1.6引入自適應的自旋鎖仆抵,自適應就意味著自旋的次數(shù)不再是固定的跟继,它是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定。線程如果自旋成功了肢础,則下次自旋的次數(shù)會更多还栓,如果自旋失敗了,則自旋的次數(shù)就會減少传轰。

  1. 某個某個鎖對象自旋成功獲取輕量級鎖剩盒,并且持有鎖的線程正在運行中,那么虛擬機就會認為這次自旋也很有可能再次成功慨蛙,進而它將允許自旋持續(xù)相對更長的時間辽聊。
  2. 如果某個對象鎖自旋很少成功獲得,下次會減少自旋的次數(shù)期贫,很快升級為重量級鎖進入阻塞狀態(tài)跟匆,避免CPU資源浪費

重量級鎖

重量級鎖涉及到了線程的阻塞,所以需要有一個容器隊列來存儲所有阻塞的線程通砍。對象鎖還支持wait方法玛臂,允許持有鎖的線程釋放鎖并進入阻塞等待狀態(tài)烤蜕,也需要一個容器隊列來存儲所有阻塞等待的線程。Monitor就是做這件事的迹冤。通過將鎖對象頭的Mark World指向Monitor對象讽营,實現(xiàn)對象頭的補充膨脹效果。Monitor對象更像是對對象頭的功能補充

重量級鎖的實現(xiàn)是依靠Monitor對象實現(xiàn)泡徙,Monitor的本質(zhì)是依賴于底層操作系統(tǒng)的MutexLock(互斥鎖)實現(xiàn)橱鹏,MutexLock會導致線程的阻塞和喚醒,操作系統(tǒng)實現(xiàn)線程之間的切換需要從用戶態(tài)到內(nèi)核態(tài)的轉(zhuǎn)換堪藐,成本非常高莉兰。同時Monitor維護了持有鎖線程、阻塞線程隊列礁竞、阻塞等待線程隊列糖荒、重入次數(shù)等信息

monitor鎖對象

monitor是線程私有的數(shù)據(jù)結(jié)構(gòu),存儲在棧中苏章,每一個線程都有一個可用monitor列表寂嘉,同時還有一個全局的可用列表,當線程可用monitor列表為空的時候會請求全局可用列表補充枫绅。泉孩??并淋?

image

Owner:初始時為NULL表示當前沒有任何線程擁有該monitor寓搬,線程加鎖成功后記錄持有鎖的線程ID,當鎖釋放后設置為NULL县耽;
EntryQ: 鏈表句喷,用于存儲所有阻塞在該鎖對象上的線程。存有兩個隊列兔毙,entry-set用于存儲正在阻塞競爭的線程唾琼,wait-set用于存儲調(diào)用wait方法等待喚醒的線程
RcThis: 表示所有阻塞或者等待在該鎖對象上的線程個數(shù)
Nest: 記錄鎖對象重入的次數(shù)
HashCode: 保存從對象頭拷貝過來的HashCode值
Candidate: 0表示沒有需要喚醒的線程1表示要喚醒一個繼任線程來競爭鎖。

對象鎖的API實現(xiàn)依賴Monitor的實現(xiàn)澎剥,Monitor提供了獲取鎖(enter)锡溯、釋放鎖(exit)、等待(wait)哑姚、notify(喚醒)祭饭、notifyAll(喚醒所有)功能

加鎖

線程CAS自旋獲取輕量級鎖失敗后,將鎖膨脹為重量級鎖

  1. 從當前線程可用Monitor列表中取出一個Monitor對象叙量,修改Mark World指向Monitor對象
  2. 修改Monitor對象的Owner為持有輕量級鎖的線程ID,Nest初始為1
  3. 修改對象頭的鎖狀態(tài)為重量級鎖(10)
  4. 當前線程加入到Monitor的entry-set隊列中倡蝙,進入阻塞狀態(tài)

當一個新的線程來競爭重量級鎖或者當阻塞隊列中的線程被喚醒,競爭重量級鎖

  1. 當前線程判斷對象頭的鎖狀態(tài)為重量級鎖(10)绞佩,然后根據(jù)Mark World引用獲取Monitor對象
  2. 當前線程調(diào)用Monitor對象的獲取鎖(enter)方法
    1. 如果Monitor鎖沒有被其他線程獲取寺鸥,當前線程成功獲取鎖猪钮,修改Monitro的Owner為當前線程
    2. 如果Monitor鎖已經(jīng)被其他線程獲取,判斷Owner是否為當前線程
      1. 如果相同胆建,重入鎖躬贡,Nest++,當前線程繼續(xù)持有鎖
      2. 如果不同眼坏,當前線程加入到entry-set中,進入阻塞狀態(tài)

釋放鎖

  1. 將Monitor對象的Next字段減去 0 ,判斷減去后的值是否為0(可能重入)
    1. Nest>0酸些,說明重入鎖還未釋放鎖宰译,當前線程繼續(xù)持有鎖
    2. Next=0, 重入鎖釋放完成,設置Owner為空魄懂,檢查rfThis是否大于0沿侈,用于判讀是否需要喚醒被阻塞的線程
      1. rfThis>0,喚醒阻塞隊列中一個被阻塞的線程市栗,該線程競爭重量級鎖
      2. rfThis=0缀拭,沒有其他線程在競爭鎖,也沒有阻塞中的線程了填帽,當前Monior已經(jīng)沒有用了蛛淋,徹底釋放鎖,將Mark World指向為null篡腌,設置鎖狀態(tài)為01(無鎖),將解除關(guān)聯(lián)的monitor對象重新放入線程可用monitor列表中

鎖粗化

在已經(jīng)持有該對象鎖的情況下褐荷,再次調(diào)用synchronized該對象,不再判斷嘹悼,如StringBuilder.append().apend()

public void test() {
  synchronized (this) {
    System.out.println("a");
  }
  synchronized (this) {//優(yōu)化后不再阻塞
    System.out.println("b");
  }
}

鎖消除

通過運行時JIT編譯器的逃逸分析來消除一些沒有操作共享變量的同步操作

public void test1() {
  Vector vector = new Vector();
  vector.add("1");//vector本身屬于線程私有的叛甫,調(diào)用add的時候會進行鎖消除
  vector.add("2");
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市杨伙,隨后出現(xiàn)的幾起案子其监,更是在濱河造成了極大的恐慌,老刑警劉巖限匣,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抖苦,死亡現(xiàn)場離奇詭異,居然都是意外死亡膛腐,警方通過查閱死者的電腦和手機睛约,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哲身,“玉大人辩涝,你說我怎么就攤上這事】碧欤” “怎么了怔揩?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵捉邢,是天一觀的道長。 經(jīng)常有香客問我商膊,道長伏伐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任晕拆,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘整吆。我一直安慰自己勇哗,他們只是感情好,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般澡腾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迹辐,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機與錄音殷绍,去河邊找鬼茶行。 笑死看锉,一個胖子當著我的面吹牛蜡感,可吹牛的內(nèi)容都是我干的恃泪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼犀斋,長吁一口氣:“原來是場噩夢啊……” “哼贝乎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起叽粹,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤览效,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后虫几,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锤灿,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年辆脸,在試婚紗的時候發(fā)現(xiàn)自己被綠了但校。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡啡氢,死狀恐怖状囱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倘是,我是刑警寧澤亭枷,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站搀崭,受9級特大地震影響叨粘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘤睹,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一升敲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轰传,春花似錦冻晤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锦茁,卻和暖如春攘轩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背码俩。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工度帮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓笨篷,卻偏偏與公主長得像瞳秽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子率翅,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359