Java多線程

概念:
  • 進(jìn)程: 每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文)变骡,進(jìn)程間的切換會(huì)有較大的開(kāi)銷(xiāo),一個(gè)進(jìn)程包含1--n個(gè)線程芭逝。
  • 線程: 進(jìn)程中負(fù)責(zé)程序執(zhí)行的執(zhí)行單元塌碌。一個(gè)進(jìn)程中至少有一個(gè)線程。
  • 多線程:解決多任務(wù)同時(shí)執(zhí)行的需求铝耻,合理使用CPU資源誊爹。多線程的運(yùn)行是根據(jù)CPU切換完成,如何切換由CPU決定瓢捉,因此多線程運(yùn)行具有不確定性频丘。
  • 線程池:基本思想還是一種對(duì)象池的思想,開(kāi)辟一塊內(nèi)存空間泡态,里面存放了眾多(未死亡)的線程搂漠,池中線程執(zhí)行調(diào)度由池管理器來(lái)處理。當(dāng)有線程任務(wù)時(shí)某弦,從池中取一個(gè)桐汤,執(zhí)行完成后線程對(duì)象歸池而克,這樣可以避免反復(fù)創(chuàng)建線程對(duì)象所帶來(lái)的性能開(kāi)銷(xiāo),節(jié)省了系統(tǒng)的資源怔毛。
創(chuàng)建線程的兩種方式
  1. 繼承Thread類(lèi)员萍,覆蓋run()方法。創(chuàng)建線程對(duì)象并用start()方法啟動(dòng)線程拣度。
  2. 實(shí)現(xiàn)Runnable接口

Thread 類(lèi)中的 start() 和 run() 方法的區(qū)別

調(diào)用 start() 方法才會(huì)啟動(dòng)新線程碎绎;如果直接調(diào)用 Thread 的 run() 方法抗果,它的行為就會(huì)和普通的方法一樣筋帖;為了在新的線程中執(zhí)行我們的代碼日麸,必須使用 Thread.start() 方法。

用 Runnable 還是 Thread

我們都知道可以通過(guò)繼承 Thread 類(lèi)或者調(diào)用 Runnable 接口來(lái)實(shí)現(xiàn)線程蚣旱,問(wèn)題是碑幅,創(chuàng)建線程哪種方式更好呢?什么情況下使用它塞绿?這個(gè)問(wèn)題很容易回答沟涨,如果你知道Java不支持類(lèi)的多重繼承,但允許你調(diào)用多個(gè)接口异吻。所以如果你要繼承其他類(lèi)裹赴,當(dāng)然是調(diào)用Runnable接口更好了

Android 系統(tǒng)接口 HandlerThread 繼承了 Thread,它是一個(gè)可以使用 Handler 的 Thread诀浪,一個(gè)具有消息循環(huán)的線程棋返。run()方法中通過(guò) Looper.prepare() 來(lái)創(chuàng)建消息隊(duì)列,通過(guò) Looper.loop() 來(lái)開(kāi)啟消息循環(huán)雷猪【ⅲ可以在 run() 方法中執(zhí)行耗時(shí)的任務(wù),而 HandlerThread 內(nèi)部創(chuàng)建了消息隊(duì)列外界需要通過(guò) Handler 的方式來(lái)通知 HandlerThread 執(zhí)行一個(gè)具體任務(wù)求摇;HandlerThread 的 run() 方法是一個(gè)無(wú)限的循環(huán)射沟,可以通過(guò)它的 quite() 或 quitSafely() 方法來(lái)終止線程的執(zhí)行殊者;

創(chuàng)建線程的第三種方式

Callable 是在 JDK1.5 增加的(Runnable在JDK1.0)。 與Runnable的主要區(qū)別是 Callable 的 call() 方法可以返回值和拋出異常验夯,而 Runnable 的 run() 方法沒(méi)有這些功能猖吴。Callable 可以返回裝載有計(jì)算結(jié)果的 Future 對(duì)象。

源碼:

    public interface Runnable {
      public void run();
    }

    public interface Callable<V> {
      V call() throws Exception;
    }

Callable無(wú)法在新線程中new Thread(Runnable r)使用挥转,Thread 類(lèi)只支持 Runnable距误。不過(guò) Callable 可以使用 ExecutorService 。

#######Future

該接口定義5個(gè)方法

  1. boolean cancel(boolean mayInterruptIfRunning):試圖取消對(duì)此任務(wù)的執(zhí)行扁位。如果任務(wù)已完成准潭、或已取消,或者由于某些其他原因而無(wú)法取消域仇,則此嘗試將失敗刑然。當(dāng)調(diào)用 cancel() 時(shí),如果調(diào)用成功暇务,而此任務(wù)尚未啟動(dòng)泼掠,則此任務(wù)將永不運(yùn)行。如果任務(wù)已經(jīng)啟動(dòng)垦细,則 mayInterruptIfRunning 參數(shù)確定是否應(yīng)該以試圖停止任務(wù)的方式來(lái)中斷執(zhí)行此任務(wù)的線程择镇。此方法返回后,對(duì) isDone() 的后續(xù)調(diào)用將始終返回 true括改。如果此方法返回 true腻豌,則對(duì) isCancelled() 的后續(xù)調(diào)用將始終返回 true。

  2. boolean isCancelled():如果在任務(wù)正常完成前將其取消嘱能,則返回 true吝梅。

  3. boolean isDone():如果任務(wù)已完成,則返回 true惹骂。 可能由于正常終止苏携、異常或取消而完成对粪,在所有這些情況中右冻,此方法都將返回 true。

  4. V get()throws InterruptedException,ExecutionException:如有必要著拭,等待計(jì)算完成纱扭,然后獲取其結(jié)果。

  5. V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException: 如有必要茫死,最多等待為使計(jì)算完成所給定的時(shí)間之后跪但,獲取其結(jié)果(如果結(jié)果可用)

#######FutureTask

FutureTask實(shí)現(xiàn)了兩個(gè)接口,Runnable和Future,所以它既可以作為Runnable被線程執(zhí)行屡久,又可以作為Future得到Callable的返回值忆首。
public class FutureTask<V> implements RunnableFuture<V> {
...
}

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

示例

Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
        return "哈哈哈";
    }
};

FutureTask<String> task = new FutureTask<String>(callable);

Thread t = new Thread(task);
t.start(); // 啟動(dòng)線程
task.cancel(true); // 取消線程
多線程

使用多線程得好處:

優(yōu)點(diǎn):

1)適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率(多個(gè)線程同時(shí)執(zhí)行)。

2)適當(dāng)?shù)奶岣吡速Y源利用率(CPU被环、內(nèi)存等)糙及。

缺點(diǎn):

1)占用一定的內(nèi)存空間。

2)線程越多CPU的調(diào)度開(kāi)銷(xiāo)越大筛欢。

3)程序的復(fù)雜度會(huì)上升浸锨。

要點(diǎn)

synchronized

synchronized是Java中的關(guān)鍵字,是一種同步鎖版姑。它修飾的對(duì)象有以下幾種:

  1. 修飾一個(gè)代碼塊柱搜,被修飾的代碼塊稱為同步語(yǔ)句塊,其作用的范圍是大括號(hào){}括起來(lái)的代碼剥险,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象聪蘸;

  2. 修飾一個(gè)方法,被修飾的方法稱為同步方法表制,其作用的范圍是整個(gè)方法健爬,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象;

  3. 修改一個(gè)靜態(tài)的方法么介,其作用的范圍是整個(gè)靜態(tài)方法娜遵,作用的對(duì)象是這個(gè)類(lèi)的所有對(duì)象;

  4. 修改一個(gè)類(lèi)壤短,其作用的范圍是synchronized后面括號(hào)括起來(lái)的部分设拟,作用主的對(duì)象是這個(gè)類(lèi)的所有對(duì)象。

wait()鸽扁、notify()蒜绽、notifyAll()

wait():
導(dǎo)致線程進(jìn)入等待狀態(tài),直到它被其他線程通過(guò)notify()或者notifyAll喚醒桶现,該方法只能在同步方法中調(diào)用。

notify():
隨機(jī)選擇一個(gè)在該對(duì)象上調(diào)用wait方法的線程鼎姊,解除其阻塞狀態(tài)骡和,該方法只能在同步方法或同步塊內(nèi)部調(diào)用。

notifyAll():
解除所有那些在該對(duì)象上調(diào)用wait方法的線程的阻塞狀態(tài)相寇,同樣該方法只能在同步方法或同步塊內(nèi)部調(diào)用慰于。

調(diào)用這三個(gè)方法中任意一個(gè),當(dāng)前線程必須是鎖的持有者唤衫,如果不是會(huì)拋出一個(gè) IllegalMonitorStateException 異常婆赠。

wait() 與 Thread.sleep(long time) 的區(qū)別

sleep():在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)拖叙。

wait() 方法使實(shí)體所處線程暫停執(zhí)行,從而使對(duì)象進(jìn)入等待狀態(tài)剃盾,直到被 notify() 方法通知或者 wait() 的等待的時(shí)間到坏逢。

sleep() 方法使持有的線程暫停運(yùn)行,從而使線程進(jìn)入休眠狀態(tài)妙黍,直到用 interrupt 方法來(lái)打斷他的休眠或者 sleep 的休眠的時(shí)間到悴侵。

wait() 方法進(jìn)入等待狀態(tài)時(shí)會(huì)釋放同步鎖,而 sleep() 方法不會(huì)釋放同步鎖拭嫁。

join()

thread.Join把指定的線程加入到當(dāng)前線程可免,可以將兩個(gè)交替執(zhí)行的線程合并為順序執(zhí)行的線程。比如在線程B中調(diào)用了線程A的Join()方法做粤,直到線程A執(zhí)行完畢后浇借,才會(huì)繼續(xù)執(zhí)行線程B。

t.join(); //使調(diào)用線程 t 在此之前執(zhí)行完畢怕品。
t.join(1000); //等待 t 線程逮刨,等待時(shí)間是1000毫秒

偽代碼

Thread t1 = new Thread(線程一);  
Thread t2 = new Thread(線程二);  
t1.start();  
t1.join(); // 等待線程一執(zhí)行完成,再執(zhí)行線程二
t2.start();

啟動(dòng) t1 后堵泽,調(diào)用了 join() 方法修己,直到 t1 的任務(wù)結(jié)束,才輪到 t2 啟動(dòng)迎罗,然后 t2 才開(kāi)始任務(wù)睬愤,兩個(gè)線程是按著嚴(yán)格的順序來(lái)執(zhí)行的。

**Thread.yield() **

Thread.yield():線程放棄運(yùn)行纹安,將CPU的控制權(quán)讓出尤辱。

yield() 方法讓出控制權(quán)后,如果優(yōu)先級(jí)高于其他的線程厢岂,還有可能馬上被系統(tǒng)的調(diào)度機(jī)制選中來(lái)運(yùn)行光督。

線程池

優(yōu)點(diǎn):

1)避免線程的創(chuàng)建和銷(xiāo)毀帶來(lái)的性能開(kāi)銷(xiāo)。

2)避免大量的線程間因互相搶占系統(tǒng)資源導(dǎo)致的阻塞現(xiàn)象塔粒。

3}能夠?qū)€程進(jìn)行簡(jiǎn)單的管理并提供定時(shí)執(zhí)行结借、間隔執(zhí)行等功能。

Java里面線程池的頂級(jí)接口是 Executor卒茬,不過(guò)真正的線程池接口是 ExecutorService船老, ExecutorService 的默認(rèn)實(shí)現(xiàn)是 ThreadPoolExecutor;
普通類(lèi) Executors 里面調(diào)用的就是 ThreadPoolExecutor圃酵。

Java通過(guò)Executors提供四種線程池

  1. newCachedThreadPool 可變尺寸的線程池柳畔,但是在以前構(gòu)造的線程可用時(shí)將重用它們。對(duì)于執(zhí)行很多短期異步任務(wù)的程序而言郭赐,這些線程池通承胶可提高程序性能。調(diào)用 execute() 將重用以前構(gòu)造的線程(如果線程可用)。如果現(xiàn)有線程沒(méi)有可用的俘陷,則創(chuàng)建一個(gè)新線程并添加到池中罗捎。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。因此岭洲,長(zhǎng)時(shí)間保持空閑的線程池不會(huì)使用任何資源宛逗。注意,可以使用 ThreadPoolExecutor 構(gòu)造方法創(chuàng)建具有類(lèi)似屬性但細(xì)節(jié)不同(例如超時(shí)參數(shù))的線程池盾剩。(只有非核心線程雷激,最大線程數(shù)非常大,所有線程都活動(dòng)時(shí)告私,會(huì)為新任務(wù)創(chuàng)建新線程屎暇,否則利用空閑線程(60s空閑時(shí)間,過(guò)了就會(huì)被回收驻粟,所以線程池中有0個(gè)線程的可能)處理任務(wù))

  2. newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池根悼,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),所有的任務(wù)是串行執(zhí)行的蜀撑,如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束挤巡,那么會(huì)有一個(gè)新的線程來(lái)替代它,保證所有任務(wù)按照指定順序(FIFO)執(zhí)行酷麦。(只有一個(gè)核心線程矿卑,確保所有任務(wù)都在同一線程中按順序完成。因此不需要處理線程同步的問(wèn)題沃饶。)

  3. newFixedThreadPool 固定大小的線程池母廷,每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小糊肤,線程池的大小一旦達(dá)到最大值就會(huì)保持不變琴昆,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程馆揉。(只有核心線程业舍,并且數(shù)量固定的,也不會(huì)被回收把介,所有線程都活動(dòng)時(shí)勤讽,因?yàn)殛?duì)列沒(méi)有限制大小,新任務(wù)會(huì)等待執(zhí)行拗踢。)

  4. newScheduledThreadPool 創(chuàng)建一個(gè)大小無(wú)限的線程池,此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求向臀。

ThreadPoolExecutor是Executors類(lèi)的底層實(shí)現(xiàn)巢墅。

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue,
                      ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 
        threadFactory, defaultHandler);

}

1)corePoolSize:線程池的核心線程數(shù),一般情況下不管有沒(méi)有任務(wù)都會(huì)一直在線程池中一直存活,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true 時(shí)君纫,閑置的核心線程會(huì)存在超時(shí)機(jī)制驯遇,如果在指定時(shí)間沒(méi)有新任務(wù)來(lái)時(shí),核心線程也會(huì)被終止蓄髓,而這個(gè)時(shí)間間隔由第3個(gè)屬性 keepAliveTime 指定叉庐。

2)maximumPoolSize:線程池所能容納的最大線程數(shù),當(dāng)活動(dòng)的線程數(shù)達(dá)到這個(gè)值后会喝,后續(xù)的新任務(wù)將會(huì)被阻塞陡叠。

3)keepAliveTime:控制線程閑置時(shí)的超時(shí)時(shí)長(zhǎng),超過(guò)則終止該線程肢执。一般情況下用于非核心線程枉阵,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true時(shí),也作用于核心線程预茄。

4)unit:用于指定 keepAliveTime 參數(shù)的時(shí)間單位兴溜,TimeUnit 是個(gè) enum 枚舉類(lèi)型,常用的有:TimeUnit.HOURS(小時(shí))耻陕、TimeUnit.MINUTES(分鐘)拙徽、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等。

5)workQueue:線程池的任務(wù)隊(duì)列诗宣,通過(guò)線程池的 execute(Runnable command) 方法會(huì)將任務(wù) Runnable 存儲(chǔ)在隊(duì)列中膘怕。

6)threadFactory:線程工廠,它是一個(gè)接口梧田,用來(lái)為線程池創(chuàng)建新線程的淳蔼。

  • ThreadPoolExecutor執(zhí)行任務(wù)時(shí)的心路歷程(以下用currentSize表示線程池中當(dāng)前線程數(shù)量):

(1)當(dāng)currentSize<corePoolSize時(shí),沒(méi)什么好說(shuō)的裁眯,直接啟動(dòng)一個(gè)核心線程并執(zhí)行任務(wù)鹉梨。

(2)當(dāng)currentSize>=corePoolSize、并且workQueue未滿時(shí)穿稳,添加進(jìn)來(lái)的任務(wù)會(huì)被安排到workQueue中等待執(zhí)行存皂。

(3)當(dāng)workQueue已滿,但是currentSize<maximumPoolSize時(shí)逢艘,會(huì)立即開(kāi)啟一個(gè)非核心線程來(lái)執(zhí)行任務(wù)旦袋。

(4)當(dāng)currentSize>=corePoolSize、workQueue已滿它改、并且currentSize>maximumPoolSize時(shí)疤孕,調(diào)用handler默認(rèn)拋出RejectExecutionExpection異常。

線程池 submit 和 execute

  1. 接收的參數(shù)不一樣

  2. submit有返回值央拖,而execute沒(méi)有

<T> Future<T> submit(Callable<T> task);

Future<?> submit(Runnable task);

void execute(Runnable command);

實(shí)現(xiàn):submit內(nèi)部調(diào)用execute祭阀,有返回值鹉戚,拋出異常。

 public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

線程池的關(guān)閉

ThreadPoolExecutor 提供了兩個(gè)方法专控,用于線程池的關(guān)閉抹凳。

shutdown():不會(huì)立即的終止線程池,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止伦腐,但再也不會(huì)接受新的任務(wù)赢底。

shutdownNow():立即終止線程池,并嘗試打斷正在執(zhí)行的任務(wù)柏蘑,并且清空任務(wù)緩存隊(duì)列幸冻,返回尚未執(zhí)行的任務(wù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辩越,一起剝皮案震驚了整個(gè)濱河市嘁扼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌黔攒,老刑警劉巖趁啸,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異督惰,居然都是意外死亡不傅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)赏胚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)访娶,“玉大人,你說(shuō)我怎么就攤上這事觉阅⊙掳蹋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵典勇,是天一觀的道長(zhǎng)劫哼。 經(jīng)常有香客問(wèn)我,道長(zhǎng)割笙,這世上最難降的妖魔是什么权烧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮伤溉,結(jié)果婚禮上般码,老公的妹妹穿的比我還像新娘。我一直安慰自己乱顾,他們只是感情好板祝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著走净,像睡著了一般扔字。 火紅的嫁衣襯著肌膚如雪囊嘉。 梳的紋絲不亂的頭發(fā)上温技,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天革为,我揣著相機(jī)與錄音,去河邊找鬼舵鳞。 笑死震檩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜓堕。 我是一名探鬼主播抛虏,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼套才!你這毒婦竟也來(lái)了迂猴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤背伴,失蹤者是張志新(化名)和其女友劉穎沸毁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體傻寂,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡息尺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疾掰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搂誉。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖静檬,靈堂內(nèi)的尸體忽然破棺而出炭懊,到底是詐尸還是另有隱情,我是刑警寧澤拂檩,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布侮腹,位于F島的核電站,受9級(jí)特大地震影響广恢,放射性物質(zhì)發(fā)生泄漏凯旋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一钉迷、第九天 我趴在偏房一處隱蔽的房頂上張望至非。 院中可真熱鬧,春花似錦糠聪、人聲如沸荒椭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)趣惠。三九已至狸棍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間味悄,已是汗流浹背草戈。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侍瑟,地道東北人唐片。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像涨颜,于是被迫代替她去往敵國(guó)和親费韭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容