Java并發(fā)編程挂绰,你需要知道的

本文大綱

1.并發(fā)編程三要素

  • 原子性
    原子,即一個(gè)不可再被分割的顆粒项乒。在Java中原子性指的是一個(gè)或多個(gè)操作要么全部執(zhí)行成功要么全部執(zhí)行失敗啰劲。
  • 有序性
    程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。(處理器可能會(huì)對(duì)指令進(jìn)行重排序)
  • 可見性
    當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí)檀何,如果其中一個(gè)線程對(duì)其作了修改蝇裤,其他線程能立即獲取到最新的值。

2. 線程的五大狀態(tài)

  • 創(chuàng)建狀態(tài)
    當(dāng)用 new 操作符創(chuàng)建一個(gè)線程的時(shí)候
  • 就緒狀態(tài)
    調(diào)用 start 方法频鉴,處于就緒狀態(tài)的線程并不一定馬上就會(huì)執(zhí)行 run 方法栓辜,還需要等待CPU的調(diào)度
  • 運(yùn)行狀態(tài)
    CPU 開始調(diào)度線程,并開始執(zhí)行 run 方法
  • 阻塞狀態(tài)
    線程的執(zhí)行過程中由于一些原因進(jìn)入阻塞狀態(tài)
    比如:調(diào)用 sleep 方法垛孔、嘗試去得到一個(gè)鎖等等??
  • 死亡狀態(tài)
    run 方法執(zhí)行完 或者 執(zhí)行過程中遇到了一個(gè)異常

3.悲觀鎖與樂觀鎖

  • 悲觀鎖:每次操作都會(huì)加鎖藕甩,會(huì)造成線程阻塞。
  • 樂觀鎖:每次操作不加鎖而是假設(shè)沒有沖突而去完成某項(xiàng)操作周荐,如果因?yàn)闆_突失敗就重試狭莱,直到成功為止,不會(huì)造成線程阻塞概作。?

4.線程之間的協(xié)作

4.1 wait/notify/notifyAll

這一組是 Object 類的方法
需要注意的是:這三個(gè)方法都必須在同步的范圍內(nèi)調(diào)用?

  • wait
    阻塞當(dāng)前線程腋妙,直到 notify 或者 notifyAll 來喚醒????

    wait有三種方式的調(diào)用
    wait()
    必要要由 notify 或者 notifyAll 來喚醒????
    wait(long timeout)
    在指定時(shí)間內(nèi),如果沒有notify或notifAll方法的喚醒讯榕,也會(huì)自動(dòng)喚醒骤素。
    wait(long timeout,long nanos)
    本質(zhì)上還是調(diào)用一個(gè)參數(shù)的方法
    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);
    }
                  ?
    
    • notify
      只能喚醒一個(gè)處于 wait 的線程
    • notifyAll
      喚醒全部處于 wait 的線程
      ?

4.2 sleep/yield/join

這一組是 Thread 類的方法

  • sleep
    讓當(dāng)前線程暫停指定時(shí)間,只是讓出CPU的使用權(quán)愚屁,并不釋放鎖

  • yield
    暫停當(dāng)前線程的執(zhí)行济竹,也就是當(dāng)前CPU的使用權(quán),讓其他線程有機(jī)會(huì)執(zhí)行集绰,不能指定時(shí)間规辱。會(huì)讓當(dāng)前線程從運(yùn)行狀態(tài)轉(zhuǎn)變?yōu)榫途w狀態(tài),此方法在生產(chǎn)環(huán)境中很少會(huì)使用到栽燕,???官方在其注釋中也有相關(guān)的說明

          /**
          * 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.
          *
          * <p> 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.
          *
          * <p> 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.
          */??
          ????
    
  • join
    等待調(diào)用 join 方法的線程執(zhí)行結(jié)束,才執(zhí)行后面的代碼
    其調(diào)用一定要在 start 方法之后(看源碼可知)?
    使用場(chǎng)景:當(dāng)父線程需要等待子線程執(zhí)行結(jié)束才執(zhí)行后面內(nèi)容或者需要某個(gè)子線程的執(zhí)行結(jié)果會(huì)用到 join 方法?

5.valitate 關(guān)鍵字

5.1 定義

java編程語言允許線程訪問共享變量改淑,為了確保共享變量能被準(zhǔn)確和一致的更新碍岔,線程應(yīng)該確保通過排他鎖單獨(dú)獲得這個(gè)變量。Java語言提供了volatile朵夏,在某些情況下比鎖更加方便蔼啦。如果一個(gè)字段被聲明成volatile,java線程內(nèi)存模型確保所有線程看到這個(gè)變量的值是一致的仰猖。

valitate是輕量級(jí)的synchronized捏肢,不會(huì)引起線程上下文的切換和調(diào)度奈籽,執(zhí)行開銷更小。

5.2 原理

1. 使用volitate修飾的變量在匯編階段鸵赫,會(huì)多出一條lock前綴指令
2. 它確保指令重排序時(shí)不會(huì)把其后面的指令排到內(nèi)存屏障之前的位置衣屏,也不會(huì)把前面的指令排到內(nèi)存屏障的后面;即在執(zhí)行到內(nèi)存屏障這句指令時(shí)辩棒,在它前面的操作已經(jīng)全部完成
3. 它會(huì)強(qiáng)制將對(duì)緩存的修改操作立即寫入主存
4. 如果是寫操作狼忱,它會(huì)導(dǎo)致其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無效

5.3 作用

內(nèi)存可見性
多線程操作的時(shí)候,一個(gè)線程修改了一個(gè)變量的值 一睁,其他線程能立即看到修改后的值
防止重排序
即程序的執(zhí)行順序按照代碼的順序執(zhí)行(處理器為了提高代碼的執(zhí)行效率可能會(huì)對(duì)代碼進(jìn)行重排序)

并不能保證操作的原子性(比如下面這段代碼的執(zhí)行結(jié)果一定不是100000)

    public class testValitate {
    public volatile int inc = 0;
    public void increase() {
        inc = inc + 1;
    }
    public static void main(String[] args) {
        final testValitate test = new testValitate();
        for (int i = 0; i < 100; i++) {
            new Thread() {
                public void run() {
                    for (int j = 0; j < 1000; j++)
                        test.increase();
                }
            }.start();
        }
        while (Thread.activeCount() > 2) {  //保證前面的線程都執(zhí)行完
            Thread.yield();
        }
        System.out.println(test.inc);
     }
   }

6. synchronized 關(guān)鍵字

確保線程互斥的訪問同步代碼

6.1 定義

synchronized 是JVM實(shí)現(xiàn)的一種鎖钻弄,其中鎖的獲取和釋放分別是
monitorenter 和 monitorexit 指令,該鎖在實(shí)現(xiàn)上分為了偏向鎖者吁、輕量級(jí)鎖和重量級(jí)鎖窘俺,其中偏向鎖在 java1.6 是默認(rèn)開啟的,輕量級(jí)鎖在多線程競(jìng)爭(zhēng)的情況下會(huì)膨脹成重量級(jí)鎖复凳,有關(guān)鎖的數(shù)據(jù)都保存在對(duì)象頭中

6.2 原理

加了 synchronized 關(guān)鍵字的代碼段瘤泪,生成的字節(jié)碼文件會(huì)多出 monitorenter 和 monitorexit 兩條指令(利用javap -verbose 字節(jié)碼文件可看到關(guān),關(guān)于這兩條指令的文檔如下:

  • monitorenter
    Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
    ? If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
    ? If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
    ? If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.?

  • monitorexit
    The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
    The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.??

加了 synchronized 關(guān)鍵字的方法染坯,生成的字節(jié)碼文件中會(huì)多一個(gè) ACC_SYNCHRONIZED 標(biāo)志位均芽,當(dāng)方法調(diào)用時(shí),調(diào)用指令將會(huì)檢查方法的 ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置单鹿,如果設(shè)置了掀宋,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體仲锄,方法執(zhí)行完后再釋放monitor劲妙。在方法執(zhí)行期間,其他任何線程都無法再獲得同一個(gè)monitor對(duì)象儒喊。 其實(shí)本質(zhì)上沒有區(qū)別镣奋,只是方法的同步是一種隱式的方式來實(shí)現(xiàn),無需通過字節(jié)碼來完成怀愧。

6.3 關(guān)于使用

  • 修飾普通方法
    同步對(duì)象是實(shí)例對(duì)象
  • 修飾靜態(tài)方法
    同步對(duì)象是類本身
  • 修飾代碼塊
    可以自己設(shè)置同步對(duì)象?

6.4 缺點(diǎn)

會(huì)讓沒有得到鎖的資源進(jìn)入Block狀態(tài)侨颈,爭(zhēng)奪到資源之后又轉(zhuǎn)為Running狀態(tài),這個(gè)過程涉及到操作系統(tǒng)用戶模式和內(nèi)核模式的切換芯义,代價(jià)比較高哈垢。Java1.6為 synchronized 做了優(yōu)化,增加了從偏向鎖到輕量級(jí)鎖再到重量級(jí)鎖的過度扛拨,但是在最終轉(zhuǎn)變?yōu)橹亓考?jí)鎖之后耘分,性能仍然較低。

7. CAS

AtomicBoolean,AtomicInteger求泰,AtomicLong以及 Lock 相關(guān)類等底層就是用 CAS實(shí)現(xiàn)的央渣,在一定程度上性能比 synchronized 更高。

7.1 什么是CAS

CAS全稱是Compare And Swap渴频,即比較替換芽丹,是實(shí)現(xiàn)并發(fā)應(yīng)用到的一種技術(shù)。操作包含三個(gè)操作數(shù) —— 內(nèi)存位置(V)枉氮、預(yù)期原值(A)和新值(B)志衍。 如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值 聊替。否則楼肪,處理器不做任何操作。

7.2 為什么會(huì)有CAS

如果只是用 synchronized 來保證同步會(huì)存在以下問題
synchronized 是一種悲觀鎖惹悄,在使用上會(huì)造成一定的性能問題春叫。在多線程競(jìng)爭(zhēng)下,加鎖泣港、釋放鎖會(huì)導(dǎo)致比較多的上下文切換和調(diào)度延時(shí)暂殖,引起性能問題。一個(gè)線程持有鎖會(huì)導(dǎo)致其它所有需要此鎖的線程掛起当纱。

7.3 實(shí)現(xiàn)原理

Java不能直接的訪問操作系統(tǒng)底層呛每,是通過native方法(JNI)來訪問。CAS底層通過Unsafe類實(shí)現(xiàn)原子性操作坡氯。

7.4 存在的問題

  • ABA問題
    什么是ABA問題晨横?比如有一個(gè) int 類型的值 N 是 1
    此時(shí)有三個(gè)線程想要去改變它:
    線程A ??:希望給 N 賦值為 2
    線程B: 希望給 N 賦值為 2
    線程C: 希望給 N 賦值為 1??
    此時(shí)線程A和線程B同時(shí)獲取到N的值1,線程A率先得到系統(tǒng)資源箫柳,將 N 賦值為 2手形,線程 B 由于某種原因被阻塞住,線程C在線程A執(zhí)行完后得到 N 的當(dāng)前值2
    此時(shí)的線程狀態(tài)
    線程A成功給 N 賦值為2
    線程B獲取到 N 的當(dāng)前值 1 希望給他賦值為 2悯恍,處于阻塞狀態(tài)
    線程C獲取當(dāng)好 N 的當(dāng)前值 2 ?????希望給他賦值為1
    ??
    然后線程C成功給N賦值為1
    ?最后線程B得到了系統(tǒng)資源库糠,又重新恢復(fù)了運(yùn)行狀態(tài),?在阻塞之前線程B獲取到的N的值是1涮毫,執(zhí)行compare操作發(fā)現(xiàn)當(dāng)前N的值與獲取到的值相同(均為1)瞬欧,成功將N賦值為了2。
    ?
    在這個(gè)過程中線程B獲取到N的值是一個(gè)舊值??罢防,雖然和當(dāng)前N的值相等黍判,但是實(shí)際上N的值已經(jīng)經(jīng)歷了一次 1到2到1的改變
    上面這個(gè)例子就是典型的ABA問題?
    怎樣去解決ABA問題
    給變量加一個(gè)版本號(hào)即可,在比較的時(shí)候不僅要比較當(dāng)前變量的值 還需要比較當(dāng)前變量的版本號(hào)篙梢。Java中AtomicStampedReference 就解決了這個(gè)問題
  • 循環(huán)時(shí)間長(zhǎng)開銷大
    在并發(fā)量比較高的情況下,如果許多線程反復(fù)嘗試更新某一個(gè)變量美旧,卻又一直更新不成功渤滞,循環(huán)往復(fù)贬墩,會(huì)給CPU帶來很大的壓力。

CAS只能保證一個(gè)共享變量的原子操作

8. AbstractQueuedSynchronizer(AQS)

AQS抽象的隊(duì)列式同步器妄呕,是一種基于狀態(tài)(state)的鏈表管理方式陶舞。state 是用CAS去修改的。它是 java.util.concurrent 包中最重要的基石绪励,要學(xué)習(xí)想學(xué)習(xí) java.util.concurrent 包里的內(nèi)容這個(gè)類是關(guān)鍵肿孵。 ReentrantLock?、CountDownLatcher疏魏、Semaphore 實(shí)現(xiàn)的原理就是基于AQS停做。想知道他怎么實(shí)現(xiàn)以及實(shí)現(xiàn)原理 可以參看這篇文章https://www.cnblogs.com/waterystone/p/4920797.html

9. Future

在并發(fā)編程我們一般使用Runable去執(zhí)行異步任務(wù),然而這樣做我們是不能拿到異步任務(wù)的返回值的大莫,但是使用Future 就可以蛉腌。使用Future很簡(jiǎn)單,只需把Runable換成FutureTask即可只厘。使用上比較簡(jiǎn)單烙丛,這里不多做介紹。

10. 線程池

如果我們使用線程的時(shí)候就去創(chuàng)建一個(gè)線程羔味,雖然簡(jiǎn)單河咽,但是存在很大的問題。如果并發(fā)的線程數(shù)量很多赋元,并且每個(gè)線程都是執(zhí)行一個(gè)時(shí)間很短的任務(wù)就結(jié)束了忘蟹,這樣頻繁創(chuàng)建線程就會(huì)大大降低系統(tǒng)的效率,因?yàn)轭l繁創(chuàng)建線程和銷毀線程需要時(shí)間们陆。線程池通過復(fù)用可以大大減少線程頻繁創(chuàng)建與銷毀帶來的性能上的損耗寒瓦。

Java中線程池的實(shí)現(xiàn)類 ThreadPoolExecutor,其構(gòu)造函數(shù)的每一個(gè)參數(shù)的含義在注釋上已經(jīng)寫得很清楚了坪仇,這里幾個(gè)關(guān)鍵參數(shù)可以再簡(jiǎn)單說一下

  • corePoolSize :核心線程數(shù)即一直保留在線程池中的線程數(shù)量杂腰,即使處于閑置狀態(tài)也不會(huì)被銷毀。要設(shè)置 allowCoreThreadTimeOut 為 true椅文,才會(huì)被銷毀喂很。
  • maximumPoolSize:線程池中允許存在的最大線程數(shù)
  • keepAliveTime :非核心線程允許的最大閑置時(shí)間,超過這個(gè)時(shí)間就會(huì)本地銷毀皆刺。
  • workQueue:用來存放任務(wù)的隊(duì)列少辣。
    • SynchronousQueue:這個(gè)隊(duì)列會(huì)讓新添加的任務(wù)立即得到執(zhí)行,如果線程池中所有的線程都在執(zhí)行羡蛾,那么就會(huì)去創(chuàng)建一個(gè)新的線程去執(zhí)行這個(gè)任務(wù)漓帅。當(dāng)使用這個(gè)隊(duì)列的時(shí)候,maximumPoolSizes一般都會(huì)設(shè)置一個(gè)最大值 Integer.MAX_VALUE
    • LinkedBlockingQueue:這個(gè)隊(duì)列是一個(gè)無界隊(duì)列。怎么理解呢忙干,就是有多少任務(wù)來我們就會(huì)執(zhí)行多少任務(wù)器予,如果線程池中的線程小于corePoolSize ,我們就會(huì)創(chuàng)建一個(gè)新的線程去執(zhí)行這個(gè)任務(wù),如果線程池中的線程數(shù)等于corePoolSize捐迫,就會(huì)將任務(wù)放入隊(duì)列中等待乾翔,由于隊(duì)列大小沒有限制所以也被稱為無界隊(duì)列。當(dāng)使用這個(gè)隊(duì)列的時(shí)候 maximumPoolSizes 不生效(線程池中線程的數(shù)量不會(huì)超過corePoolSize)施戴,所以一般都會(huì)設(shè)置為0反浓。
    • ArrayBlockingQueue:這個(gè)隊(duì)列是一個(gè)有界隊(duì)列≡藁可以設(shè)置隊(duì)列的最大容量雷则。當(dāng)線程池中線程數(shù)大于或者等于 maximumPoolSizes 的時(shí)候,就會(huì)把任務(wù)放到這個(gè)隊(duì)列中懈玻,當(dāng)當(dāng)前隊(duì)列中的任務(wù)大于隊(duì)列的最大容量就會(huì)丟棄掉該任務(wù)交由 RejectedExecutionHandler 處理巧婶。

最后,本文主要對(duì)Java并發(fā)編程開發(fā)需要的知識(shí)點(diǎn)作了簡(jiǎn)單的講解涂乌,這里每一個(gè)知識(shí)點(diǎn)都可以用一篇文章去講解艺栈,由于篇幅原因不能對(duì)每一個(gè)知識(shí)點(diǎn)都詳細(xì)介紹,我相信通過本文你會(huì)對(duì)Java的并發(fā)編程會(huì)有更近一步的了解湾盒。如果您發(fā)現(xiàn)還有缺漏或者有錯(cuò)誤的地方湿右,可以在評(píng)論區(qū)補(bǔ)充,謝謝罚勾。

相關(guān)鏈接

版權(quán)聲明:本文為博主原創(chuàng)文章尖殃,未經(jīng)博主允許不得轉(zhuǎn)載丈莺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市送丰,隨后出現(xiàn)的幾起案子缔俄,更是在濱河造成了極大的恐慌,老刑警劉巖器躏,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俐载,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡登失,警方通過查閱死者的電腦和手機(jī)遏佣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揽浙,“玉大人状婶,你說我怎么就攤上這事意敛。” “怎么了太抓?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵空闲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我走敌,道長(zhǎng),這世上最難降的妖魔是什么逗噩? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任掉丽,我火速辦了婚禮,結(jié)果婚禮上异雁,老公的妹妹穿的比我還像新娘捶障。我一直安慰自己,他們只是感情好纲刀,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布项炼。 她就那樣靜靜地躺著,像睡著了一般示绊。 火紅的嫁衣襯著肌膚如雪锭部。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天面褐,我揣著相機(jī)與錄音拌禾,去河邊找鬼。 笑死展哭,一個(gè)胖子當(dāng)著我的面吹牛湃窍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匪傍,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼您市,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了役衡?” 一聲冷哼從身側(cè)響起茵休,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎映挂,沒想到半個(gè)月后泽篮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柑船,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年帽撑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鞍时。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亏拉,死狀恐怖扣蜻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情及塘,我是刑警寧澤莽使,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站笙僚,受9級(jí)特大地震影響芳肌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肋层,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一亿笤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧栋猖,春花似錦净薛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雌团,卻和暖如春燃领,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辱姨。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工柿菩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人雨涛。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓枢舶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親替久。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凉泄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361