多線程

1. 什么是線程

線程(thread)是指一個任務從頭至尾的執(zhí)行流仅淑,線程提供一個運行任務的機制称勋,對于java而言,一個程序中可以并發(fā)的執(zhí)行多個線程漓糙,這些線程可以在多處理器系統(tǒng)上同時運行铣缠。當程序作為一個應用程序運行時,java解釋器為main()方法啟動一個線程昆禽。

2. 線程和進程

區(qū)別和聯(lián)系:
1. 進程中可以包含多個線程蝗蛙,線程必須存在于某個進程實體中

2. 進程在進行上下文切換時由于要切換頁表,往往伴隨者頁調(diào)
   度醉鳖,因此開銷比較大捡硅,而線程在進行上下文切換時,由于僅
   涉及與自身相關的寄存器狀態(tài)和棧的信息(線程的上下文環(huán)
   境主要包含寄存器的值盗棵、程序計數(shù)器壮韭、棧指針)北发,因此開銷
   比較小喷屋;

3. 系統(tǒng)在運行時會為每個進程分配不同的內(nèi)存區(qū)域琳拨,但是不會
   為線程分配內(nèi)存,同一個進程中的各個線程共享該進程的內(nèi)
   存區(qū)域屯曹。

4. 與進程的控制表PCB相似狱庇,線程也有自己的控制表TCB,但是
   TCB中所保存的線程狀態(tài)比PCB表中少多了恶耽。

5. 獨立進程間的通信要與核心交互密任,而由于同一進程中的線程
   共享內(nèi)存,它們之間的通信就不需要調(diào)用核心偷俭。

3. 線程創(chuàng)建

  • 需要從Java.lang.Thread類派生一個新的線程類浪讳,重載它的run()方法;
  • 實現(xiàn)Runnalbe接口涌萤,重載Runnalbe接口中的run()方法淹遵。
兩者區(qū)別:

使用實現(xiàn)Runnable接口方式創(chuàng)建線程可以共享同一個目標對象(TreadDemo1 tt=new TreadDemo1();),實現(xiàn)了多個相同線程處理同一份資源.而繼承Thread創(chuàng)建線程的方式形葬,new出了兩個任務類對象合呐,有各自的成員變量,相互之間不干擾笙以。

一淌实、采用繼承Thread類方式:

(1)優(yōu)點:編寫簡單,如果需要訪問當前線程猖腕,無需使用Thread.currentThread()方法拆祈,直接使用this,即可獲得當前線程倘感。
(2)缺點:因為線程類已經(jīng)繼承了Thread類放坏,所以不能再繼承其他的父類。

二老玛、采用實現(xiàn)Runnable接口方式:

(1)優(yōu)點:線程類只是實現(xiàn)了Runable接口淤年,還可以繼承其他的類。在這種方式下蜡豹,可以多個線程共享同一個目標對象麸粮,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU代碼和數(shù)據(jù)分開镜廉,形成清晰的模型弄诲,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?br> (2)缺點:編程稍微復雜,如果需要訪問當前線程娇唯,必須使用Thread.currentThread()方法齐遵。

4. 線程的生命周期

Paste_Image.png

(1)生命周期的五種狀態(tài)

新建(new Thread)
當創(chuàng)建Thread類的一個實例(對象)時寂玲,此線程進入新建狀態(tài)(未被啟動)。
  例如:Thread t1=new Thread();

就緒(runnable)
線程已經(jīng)被啟動梗摇,正在等待被分配給CPU時間片拓哟,也就是說此時線程正在就緒隊列中排隊等候得到CPU資源。例如:t1.start();

運行(running)
線程獲得CPU資源正在執(zhí)行任務(run()方法)留美,此時除非此線程自動放棄CPU資源或者有優(yōu)先級更高的線程進入彰檬,線程將一直運行到結束伸刃。

死亡(dead)
當線程執(zhí)行完畢或被其它線程殺死谎砾,線程就進入死亡狀態(tài),這時線程不可能再進入就緒狀態(tài)等待執(zhí)行捧颅。
  自然終止:正常運行run()方法后終止
  異常終止:調(diào)用stop()方法讓一個線程終止運行

堵塞(blocked)
由于某種原因?qū)е抡谶\行的線程讓出CPU并暫停自己的執(zhí)行景图,即進入堵塞狀態(tài)。
  正在睡眠:用sleep(long t) 方法可使線程進入睡眠方式碉哑。一個睡眠著的線程在指定的時間過去可進入就緒狀態(tài)挚币。
  正在等待:調(diào)用wait()方法。(調(diào)用notify()方法回到就緒狀態(tài))
  被另一個線程所阻塞:調(diào)用suspend()方法扣典。(調(diào)用resume()方法恢復)

5. 線程安全

1). 什么是線程安全?線程安全是怎么完成的(原理)?

 線程安全就是說多線程訪問同一代碼妆毕,不會產(chǎn)生不確定的結果。編寫線程安全的代碼是低依靠線程同步贮尖。

2). 線程安全的類:

StringBuffer:線程安全的可變字符序列笛粘。一個類似于 String的字符串緩沖區(qū),但不能修改湿硝。
Vector:Vector類可以實現(xiàn)可增長的對象數(shù)組薪前。
Hashtable:此類實現(xiàn)一個哈希表,該哈希表將鍵映射到相應的值关斜。任何非 null對象都可以用作鍵或值示括。

API 搜索Collections,查看方法

A:public static <T> Collection<T> synchronizedCollection(Collection<T> c):返回指定 collection 支持的同步(線程安全的)collection痢畜。

B:public static <T> List<T> synchronizedList(List<T> list):返回指定列表支持的同步(線程安全的)列表垛膝。

C:public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m):返回由指定映射支持的同步(線程安全的)映射。

D:public static <T> Set<T> synchronizedSet(Set<T> s):返回指定 set 支持的同步(線程安全的)set丁稀。

E:public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m):返回指定有序映射支持的同步(線程安全的)有序映射吼拥。

F:public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s):返回指定有序 set 支持的同步(線程安全的)有序 set。

6.線程鎖

1. synchronized

把代碼塊聲明為 synchronized二驰,有兩個重要后果扔罪,通常是指該代碼具有 原子性(atomicity)可見性(visibility)
1 原子性
  原子性意味著個時刻桶雀,只有一個線程能夠執(zhí)行一段代碼矿酵,這段代碼通過一個monitor object保護唬复。從而防止多個線程在更新共享狀態(tài)時相互沖突。

2 可見性
  可見性則更為微妙全肮,它要對付內(nèi)存緩存和編譯器優(yōu)化的各種反常行為敞咧。它必須確保釋放鎖之前對共享數(shù)據(jù)做出的更改對于隨后獲得該鎖的另一個線程是可見的 。

作用:如果沒有同步機制提供的這種可見性保證辜腺,線程看到的共享變量可能是修改前的值或不一致的值休建,這將引發(fā)許多嚴重問題。

原理:當對象獲取鎖時评疗,它首先使自己的高速緩存無效测砂,這樣就可以保證直接從主內(nèi)存中裝入變量。 同樣百匆,在對象釋放鎖之前砌些,它會刷新其高速緩存,強制使已做的任何更改都出現(xiàn)在主內(nèi)存中加匈。 這樣存璃,會保證在同一個鎖上同步的兩個線程看到在 synchronized 塊內(nèi)修改的變量的相同值。

一般來說雕拼,線程以某種不必讓其他線程立即可以看到的方式(不管這些線程在寄存器中纵东、在處理器特定的緩存中,還是通過指令重排或者其他編譯器優(yōu)化)啥寇,不受緩存變量值的約束偎球,但是如果開發(fā)人員使用了同步,那么運行庫將確保某一線程對變量所做的更新先于對現(xiàn)有synchronized塊所進行的更新示姿,當進入由同一監(jiān)控器(lock)保護的另一個synchronized塊時甜橱,將立刻可以看到這些對變量所做的更新。類似的規(guī)則也存在于volatile變量上栈戳。

——volatile只保證可見性岂傲,不保證原子性!

synchronize的限制

synchronized是不錯子檀,但它并不完美镊掖。它有一些功能性的限制:

  1. 它無法中斷一個正在等候獲得鎖的線程;
  2. 也無法通過投票得到鎖褂痰,如果不想等下去亩进,也就沒法得到鎖;
  3. 同步還要求鎖的釋放只能在與獲得鎖所在的堆棧幀相同的堆棧幀中進行缩歪,多數(shù)情況下归薛,這沒問題(而且與異常處理交互得很好),但是,確實存在一些非塊結構的鎖定更合適的情況主籍。

2.ReentrantLock

Java.util.concurrent.lock 中的Lock框架是鎖定的一個抽象习贫,它允許把鎖定的實現(xiàn)作為 Java 類,而不是作為語言的特性來實現(xiàn)千元。這就為Lock的多種實現(xiàn)留下了空間苫昌,各種實現(xiàn)可能有不同的調(diào)度算法、性能特性或者鎖定語義幸海。
ReentrantLock類實現(xiàn)了Lock祟身,它擁有與synchronized 相同的并發(fā)性和內(nèi)存語義,但是添加了類似鎖投票物独、定時鎖等候可中斷鎖等候的一些特性袜硫。此外,它還提供了在激烈爭用情況下更佳的性能议纯。(換句話說父款,當許多線程都想訪問共享資源時,JVM 可以花更少的時候來調(diào)度線程瞻凤,把更多時間用在執(zhí)行線程上。

class Outputter1 {    
    private Lock lock = new ReentrantLock();// 鎖對象    
  
    public void output(String name) {           
        lock.lock();      // 得到鎖    
  
        try {    
            for(int i = 0; i < name.length(); i++) {    
                System.out.print(name.charAt(i));    
            }    
        } finally {    
            lock.unlock();// 釋放鎖    
        }    
    }    
} 

區(qū)別:

需要注意的是世杀,用sychronized修飾的方法或者語句塊在代碼執(zhí)行完之后鎖自動釋放阀参,而是用Lock需要我們手動釋放鎖,所以為了保證鎖最終被釋放(發(fā)生異常情況)瞻坝,要把互斥區(qū)放在try內(nèi)蛛壳,釋放鎖放在finally內(nèi)!所刀!

3.讀寫鎖ReadWriteLock

上例中展示的是和synchronized相同的功能衙荐,那Lock的優(yōu)勢在哪里?
例如一個類對其內(nèi)部共享數(shù)據(jù)data提供了get()和set()方法浮创,如果用synchronized忧吟,則代碼如下:

class syncData {        
    private int data;// 共享數(shù)據(jù)        
    public synchronized void set(int data) {    
        System.out.println(Thread.currentThread().getName() + "準備寫入數(shù)據(jù)");    
        try {    
            Thread.sleep(20);    
        } catch (InterruptedException e) {    
            e.printStackTrace();    
        }    
        this.data = data;    
        System.out.println(Thread.currentThread().getName() + "寫入" + this.data);    
    }       
    public synchronized  void get() {    
        System.out.println(Thread.currentThread().getName() + "準備讀取數(shù)據(jù)");    
        try {    
            Thread.sleep(20);    
        } catch (InterruptedException e) {    
            e.printStackTrace();    
        }    
        System.out.println(Thread.currentThread().getName() + "讀取" + this.data);    
    }    
}    

然后寫個測試類來用多個線程分別讀寫這個共享數(shù)據(jù):

public static void main(String[] args) {    
//        final Data data = new Data();    
          final syncData data = new syncData();    
//        final RwLockData data = new RwLockData();    
          
        //寫入  
        for (int i = 0; i < 3; i++) {    
            Thread t = new Thread(new Runnable() {    
                @Override  
        public void run() {    
                    for (int j = 0; j < 5; j++) {    
                        data.set(new Random().nextInt(30));    
                    }    
                }    
            });  
            t.setName("Thread-W" + i);  
            t.start();  
        }    
        //讀取  
        for (int i = 0; i < 3; i++) {    
            Thread t = new Thread(new Runnable() {    
                @Override  
        public void run() {    
                    for (int j = 0; j < 5; j++) {    
                        data.get();    
                    }    
                }    
            });    
            t.setName("Thread-R" + i);  
            t.start();  
        }    
    }    

運行結果:

Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入0  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入1  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R2讀取1  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R2讀取1  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R2讀取1  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R2讀取1  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R2讀取1  
Thread-R0準備讀取數(shù)據(jù) //R0和R2可以同時讀取,不應該互斥斩披!  
Thread-R0讀取1  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R0讀取1  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R0讀取1  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R0讀取1  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R0讀取1  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入18  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入16  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入19  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入21  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入4  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入10  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入4  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入1  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入14  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入2  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入4  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入20  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入29  

現(xiàn)在一切都看起來很好溜族!各個線程互不干擾!等等垦沉。煌抒。讀取線程和寫入線程互不干擾是正常的,但是兩個讀取線程是否需要互不干擾厕倍?寡壮?
對!讀取線程不應該互斥!

我們可以用讀寫鎖ReadWriteLock實現(xiàn):

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class Data {        
    private int data;// 共享數(shù)據(jù)    
    private ReadWriteLock rwl = new ReentrantReadWriteLock();       
    public void set(int data) {    
        rwl.writeLock().lock();// 取到寫鎖    
        try {    
            System.out.println(Thread.currentThread().getName() + "準備寫入數(shù)據(jù)");    
            try {    
                Thread.sleep(20);    
            } catch (InterruptedException e) {    
                e.printStackTrace();    
            }    
            this.data = data;    
            System.out.println(Thread.currentThread().getName() + "寫入" + this.data);    
        } finally {    
            rwl.writeLock().unlock();// 釋放寫鎖    
        }    
    }       
  
    public void get() {    
        rwl.readLock().lock();// 取到讀鎖    
        try {    
            System.out.println(Thread.currentThread().getName() + "準備讀取數(shù)據(jù)");    
            try {    
                Thread.sleep(20);    
            } catch (InterruptedException e) {    
                e.printStackTrace();    
            }    
            System.out.println(Thread.currentThread().getName() + "讀取" + this.data);    
        } finally {    
            rwl.readLock().unlock();// 釋放讀鎖    
        }    
    }    
} 

測試結果:

Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入9  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入24  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入12  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入22  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入15  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入6  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入13  
Thread-W0準備寫入數(shù)據(jù)  
Thread-W0寫入0  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入23  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入24  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入24  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入17  
Thread-W2準備寫入數(shù)據(jù)  
Thread-W2寫入11  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R0讀取11  
Thread-R1讀取11  
Thread-R2讀取11  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入18  
Thread-W1準備寫入數(shù)據(jù)  
Thread-W1寫入1  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R2讀取1  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R0讀取1  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R0讀取1  
Thread-R2讀取1  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R0讀取1  
Thread-R2讀取1  
Thread-R1讀取1  
Thread-R0準備讀取數(shù)據(jù)  
Thread-R1準備讀取數(shù)據(jù)  
Thread-R2準備讀取數(shù)據(jù)  
Thread-R1讀取1  
Thread-R2讀取1  
Thread-R0讀取1  

與互斥鎖定相比况既,讀-寫鎖定允許對共享數(shù)據(jù)進行更高級別的并發(fā)訪問屋群。雖然一次只有一個線程(writer 線程)可以修改共享數(shù)據(jù),但在許多情況下坏挠,任何數(shù)量的線程可以同時讀取共享數(shù)據(jù)(reader 線程)
從理論上講芍躏,與互斥鎖定相比,使用讀-寫鎖定所允許的并發(fā)性增強將帶來更大的性能提高降狠。

在實踐中对竣,只有在多處理器上并且只在訪問模式適用于共享數(shù)據(jù)時,才能完全實現(xiàn)并發(fā)性增強榜配》裎常——例如,某個最初用數(shù)據(jù)填充并且之后不經(jīng)常對其進行修改的 collection蛋褥,因為經(jīng)常對其進行搜索(比如搜索某種目錄)临燃,所以這樣的 collection 是使用讀-寫鎖定的理想候選者。

4.volatile

用volatile修飾的變量烙心,線程在每次使用變量的時候膜廊,都會讀取變量修改后的最的值。volatile很容易被誤用淫茵,用來進行原子性操作爪瓜。

5. 讀寫鎖和互斥鎖

讀寫鎖特點:
1)多個讀者可以同時進行讀
2)寫者必須互斥(只允許一個寫者寫,也不能讀者寫者同時進行)
3)寫者優(yōu)先于讀者(一旦有寫者匙瘪,則后續(xù)讀者必須等待铆铆,喚醒時優(yōu)先考慮寫者)
互斥鎖特點:
一次只能一個線程擁有互斥鎖,其他線程只有等待

7.

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末丹喻,一起剝皮案震驚了整個濱河市薄货,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碍论,老刑警劉巖谅猾,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骑冗,居然都是意外死亡赊瞬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門贼涩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巧涧,“玉大人,你說我怎么就攤上這事遥倦“” “怎么了占锯?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缩筛。 經(jīng)常有香客問我消略,道長,這世上最難降的妖魔是什么瞎抛? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任艺演,我火速辦了婚禮,結果婚禮上桐臊,老公的妹妹穿的比我還像新娘胎撤。我一直安慰自己,他們只是感情好断凶,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布伤提。 她就那樣靜靜地躺著,像睡著了一般认烁。 火紅的嫁衣襯著肌膚如雪肿男。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天却嗡,我揣著相機與錄音舶沛,去河邊找鬼。 笑死稽穆,一個胖子當著我的面吹牛冠王,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舌镶,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼豪娜!你這毒婦竟也來了餐胀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瘤载,失蹤者是張志新(化名)和其女友劉穎否灾,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸣奔,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡墨技,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挎狸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扣汪。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锨匆,靈堂內(nèi)的尸體忽然破棺而出崭别,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布茅主,位于F島的核電站舞痰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诀姚。R本人自食惡果不足惜响牛,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赫段。 院中可真熱鬧呀打,春花似錦、人聲如沸瑞佩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炬丸。三九已至瘫寝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稠炬,已是汗流浹背焕阿。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留首启,地道東北人暮屡。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像毅桃,于是被迫代替她去往敵國和親褒纲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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