聊聊kotlin.coroutines【java協(xié)程】(1)

kotlin這個夏天java最有競爭力的語言。關(guān)于它的語法糖在這就不一一闡述了,畢竟它能甜死你。
先說說什么是協(xié)程吧,用戶態(tài)的子線程,輕量級,進(jìn)程->線程->協(xié)程。

進(jìn)程、線程、協(xié)程的關(guān)系和區(qū)別:
進(jìn)程擁有自己獨(dú)立的堆和棧,既不共享堆,亦不共享?xiàng)#M(jìn)程由操作系統(tǒng)調(diào)度。
線程擁有自己獨(dú)立的棧和共享的堆,共享堆,不共享?xiàng)#€程亦由操作系統(tǒng)調(diào)度(標(biāo)準(zhǔn)線程是的)。
協(xié)程和線程一樣共享堆,不共享?xiàng)#瑓f(xié)程由程序員在協(xié)程的代碼里顯示調(diào)度。

協(xié)程的好處如下:
1.減少cpu線程上下文切換的開銷
2.降低了內(nèi)存消耗;
3.提高了cpu緩存命中率;
4.整體上提高了性能;
5.不提高硬件的前提下,提升了系統(tǒng)的負(fù)載能力。

只需要極少的棧內(nèi)存(大概是4~5KB),默認(rèn)情況下,線程棧的大小為1MB,一個線程可以開啟數(shù)十萬的協(xié)程,線程占用的內(nèi)存開銷遠(yuǎn)比協(xié)程要大得多。
golang原生就實(shí)現(xiàn)了協(xié)程,由runtime自行管理,一個go關(guān)鍵字就能開啟goroutine。簡直完美,但是今天要講的不是golang。

總之,協(xié)程就是便宜,廉價,高效的代名詞。

java里面要擁有這種高性能的協(xié)程,要通過第三方包來實(shí)現(xiàn)quasarcomsatkilim

上面這三位,就是目前所有java里面能快速實(shí)現(xiàn)coroutines的jar。
quasar:通過織入java字節(jié)碼的方式,改變字節(jié)碼結(jié)果,來使用,javaagent的方式
comsat:quasar的包裝版本,提供輕量化的包裝能快速使用。
kilim:和quasar一樣,也要織入字節(jié)碼來使用

但都有一個問題,必須預(yù)先給到注解,以上都能通過編譯,但是到了linux環(huán)境,需要通過javaagent,因字節(jié)碼被改寫,無法追蹤具體問題。協(xié)程管理是個大問題,會被線程kill,無故消失,筆者通過大半個月的實(shí)驗(yàn),發(fā)現(xiàn)它們無法通過大部分環(huán)境,因而放棄。

kotlin.corouties

kotlin.corouties真是個非常好的api。語法簡化,可以和golang的go關(guān)鍵字有得一拼。但在目前的kotlin api中是實(shí)驗(yàn)性質(zhì),不過已經(jīng)具備上生產(chǎn)環(huán)境的能力,預(yù)計會在1.1.5中正式發(fā)布。因kotlin和java可以混編,所以coroutines是個下個高并發(fā)必備的知識點(diǎn)了。

kotlin.corouties調(diào)度器

CommonPool 調(diào)度器默認(rèn)是通過fork/join的方式實(shí)現(xiàn),目前還不提供接口,做自定義實(shí)現(xiàn)
launch(CommonPool)

Represents common pool of shared threads as coroutine dispatcher for compute-intensive tasks.
It uses[java.util.concurrent.ForkJoinPool]when available, which implements efficient work-stealing algorithm for its queues, so every coroutine resumption is dispatched as a separate task even when it already executes inside the pool.When available, it wraps ForkJoinPool.commonPool and provides a similar shared pool where not.

也就是說,kotlin的協(xié)程是并行調(diào)度的,關(guān)于fork/join也可以單獨(dú)開一章講了,暫不表。

Unconfined 調(diào)度器,默認(rèn)是主線程調(diào)度 ,無限制啟動協(xié)程,一旦協(xié)程睡了或者掛了,會啟動新的協(xié)程

launch(Unconfined)

A coroutine dispatcher that is not confined to any specific thread.
It executes initial continuation of the coroutine right here in the current call-frame
and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
mandating any specific threading policy.

Note, that if you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
but still want to execute it in the current call-frame until its first suspension, then you can use
an optional [CoroutineStart] parameter in coroutine builders like [launch] and [async] setting it to the
the value of [CoroutineStart.UNDISPATCHED].

ThreadPoolDispatcher.newSingleThreadContext調(diào)度器,單個線程的調(diào)度器

launch(newSingleThreadContext("MyOwnThread"))

Creates new coroutine execution context with the a single thread and built-in [yield] and [delay] support.
All continuations are dispatched immediately when invoked inside the thread of this context.
Resources of this pool (its thread) are reclaimed when job of this context is cancelled.
The specified [name] defines the name of the new thread.
An optional [parent] job may be specified upon creation.

launch(newFixedThreadPoolContext(100,"MyOwnThread")) 調(diào)度器,指定線程數(shù)量的調(diào)度器

Creates new coroutine execution context with the fixed-size thread-pool and built-in [yield] and [delay] support.
All continuations are dispatched immediately when invoked inside the threads of this context.
Resources of this pool (its threads) are reclaimed when job of this context is cancelled.
The specified [name] defines the names of the threads.
An optional [parent] job may be specified upon creation.

默認(rèn)請全部使用launch(CommonPool),有特殊的限制問題,再考慮其他的調(diào)度器

launch(CommonPool) 異步協(xié)程開啟

async(CommonPool) 同步協(xié)程開啟

官方示例的Hello,World!,歡迎進(jìn)入kotlin協(xié)程的世界

fun main(args: Array<String>) {
    launch(CommonPool) { // create new coroutine in common thread pool
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello,") // main function continues while coroutine is delayed
    Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}

kotlin中的sleep將不能暫停協(xié)程了,是個大坑,后面會講到。

launch 啟動協(xié)程,默認(rèn)情況下直接開始執(zhí)行,也可以顯式執(zhí)行

var job= launch(CommonPool) 
  if(job.isActive){
          job.cancel()
       }else{
            job.start()
     }

job任務(wù)可以根據(jù)需要什么時候開始執(zhí)行,是否存活,取消等,提供了一系列api
有個小事,kotlin去掉了; 估計這個又可以引發(fā)一波大戰(zhàn)

CommonPool 調(diào)度器
delay將會暫停1秒?yún)f(xié)程運(yùn)行,
printlin是kotlin的打印方法,等同于System.out.printlin
Thread.sleep 這句只能暫停啟動協(xié)程的線程

fun main(args: Array<String>) = runBlocking<Unit> {
    val job = launch(CommonPool) { // create new coroutine and keep a reference to its Job
        delay(1000L)
        println("World!")
    }
    println("Hello,")
    job.join() // wait until child coroutine completes
}

runBlocking<Unit> 啟動一個非阻塞并且無返回值的任務(wù)
job.join() 等待協(xié)程任務(wù)完成

fun main(args: Array<String>) = runBlocking<Unit> {
    val job = launch(CommonPool) {
            doWorld() 
    }
    println("Hello,")
    job.join()
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}

這個講suspend 關(guān)鍵字,為的是代碼分離,不然就只能在 launch(CommonPool){}內(nèi)部用delay來睡協(xié)程了,去掉了suspend是無法在其他方法調(diào)用delay睡協(xié)程了,直接編譯錯誤。

fun main(args: Array<String>) = runBlocking<Unit> {
    val jobs = List(100_000) { // create a lot of coroutines and list their jobs
        launch(CommonPool) {
            delay(1000L)
            print(".")
        }
    }
    jobs.forEach { it.join() } // wait for all jobs to complete
}

這個例子比較搞,啟動100K的協(xié)程,如果你像作者一樣,2G內(nèi)存的渣機(jī)可能直接out-of-memory error,像筆者這樣的8G大內(nèi)存,是沒有一點(diǎn)問題的。輕松愉快500ms執(zhí)行完畢。

這個例子也是為了展示協(xié)程的輕量級和強(qiáng)悍,線程別說100K,就算10K,你的CPU和內(nèi)存分分鐘炸了,只能重啟。

fun main(args: Array<String>) = runBlocking<Unit> {
    val job = launch(CommonPool) {
        var nextPrintTime = 0L
        var i = 0
        while (isActive) { // cancellable computation loop
            val currentTime = System.currentTimeMillis()
            if (currentTime >= nextPrintTime) {
                println("I'm sleeping ${i++} ...")
                nextPrintTime = currentTime + 500L
            }
        }
    }
    delay(1300L) // delay a bit
    println("main: I'm tired of waiting!")
    job.cancel() // cancels the job
    delay(1300L) // delay a bit to see if it was cancelled....
    println("main: Now I can quit.")
}

delay的例子太多,單獨(dú)講一個。啟動了一個協(xié)程任務(wù)去計算當(dāng)前的時間,然后你會發(fā)現(xiàn)協(xié)程內(nèi)置了一個isActive屬性,這也是線程內(nèi)部唯三的三大內(nèi)置屬性之一。其他的兩個為context和coroutineContext,不過context已經(jīng)被放棄了,大概是作者覺得context,詞不達(dá)意吧,從這點(diǎn)也可以發(fā)現(xiàn)kotlin不會隨意的刪除api,而是通過重命名,重載的方式提供新的。

isActive:如果協(xié)程處于存活或任務(wù)未完成,狀態(tài)就返回true,如果取消或已完成,則返回false

例子的意思也很明顯告訴你如果任務(wù)在delay時間內(nèi)未被cancel則一直計算下去并打印三次I'm sleeping,然后任務(wù)被cancel,協(xié)程取消。主線程輸出main: Now I can quit

fun main(args: Array<String>) = runBlocking<Unit> {
    val job = launch(CommonPool) {
        try {
            repeat(1000) { i ->
                println("I'm sleeping $i ...")
                delay(500L)
            }
        } finally {
            run(NonCancellable) {
                println("I'm running finally")
                delay(1000L)
                println("And I've just delayed for 1 sec because I'm non-cancellable")
            }
        }
    }
    delay(1300L) // delay a bit
    println("main: I'm tired of waiting!")
    job.cancel() // cancels the job
    delay(1300L) // delay a bit to ensure it was cancelled indeed
    println("main: Now I can quit.")
}

這個例子講的不可取消, run(NonCancellable)+finally=絕對執(zhí)行的代碼
run(NonCancellable)協(xié)程內(nèi)部啟動一個新的協(xié)程,并且不能取消,霸道總裁般的代碼
run...{}內(nèi)可以使用coroutineContext,跟上一級的協(xié)程塊代碼做交互。

fun main(args: Array<String>) = runBlocking<Unit> {
    withTimeout(1300L) {
        repeat(1000) { i ->
            println("I'm sleeping $i ...")
            delay(500L)
        }
    }
}

repeat(1000) :迭代器,輸入要迭代的次數(shù):1000次
withTimeout(1300L) 時間1.3秒。
這里講這個wiathTimeout主要是為了控制協(xié)程的超時時間,避免協(xié)程,一直在活動。雖然便宜,不代表能讓任務(wù)一直執(zhí)行下去,到了超時的時間會直接拋出異常

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 29
}
fun main(args: Array<String>) = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = async(CommonPool, CoroutineStart.LAZY) { doSomethingUsefulOne() }
        val two = async(CommonPool, CoroutineStart.LAZY) { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
}

三個例子
measureTimeMillis :返回代碼塊的執(zhí)行耗時,比起java,話就是少,就是這么屌

CoroutineStart:協(xié)程的執(zhí)行模式(async和launch都可以用)

LAZY
懶加載

DEFAULT 默認(rèn)的模式
默認(rèn) - 根據(jù)其上下文立即執(zhí)行。

ATOMIC
根據(jù)其上下文原則(不可取消)計劃協(xié)調(diào)執(zhí)行。
跟[DEFAULT]類似,但協(xié)程在開始執(zhí)行前無法取消。

UNDISPATCHED
未分派:暫不明白用途

println("The answer is ${one.await() + two.await()}")
kotlin執(zhí)行計算可在字符串中一起計算
.await實(shí)際拿到的是協(xié)程返回的值,在例子中也就是13和29


suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 20
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 20
}


// The result type of asyncSomethingUsefulOne is Deferred<Int>
fun asyncSomethingUsefulOne() = async(CommonPool) {
    doSomethingUsefulOne()
}

// The result type of asyncSomethingUsefulTwo is Deferred<Int>
fun asyncSomethingUsefulTwo() = async(CommonPool)  {
    doSomethingUsefulTwo()
}


// note, that we don't have `runBlocking` to the right of `main` in this example
fun main(args: Array<String>) {
    val time = measureTimeMillis {
        // we can initiate async actions outside of a coroutine
        val one = asyncSomethingUsefulOne()
        val two = asyncSomethingUsefulTwo()
        // but waiting for a result must involve either suspending or blocking.
        // here we use `runBlocking { ... }` to block the main thread while waiting for the result
        runBlocking {
            println("The answer is ${one.await() + two.await()}")
        }
    }
    println("Completed in $time ms")
}

runBlocking{}是個同步非阻塞的代碼塊執(zhí)行器,能統(tǒng)一拿到coroutines的返回值,支持泛型和接受返回參,多個或單個協(xié)程一旦啟動后我們要拿返回值不僅可以用await,也可以用runBlocking

      var result= runBlocking<Int> {
            var resultint = one.await() + two.await()
            println("The answer is resultint="+resultint)
            //基本類型直接這樣寫就可以
            resultint
        }
     println(result)

============================================================================

fun main(args: Array<String>) = runBlocking<Unit> {
    val jobs = arrayListOf<Job>()
    jobs += launch(Unconfined) { // not confined -- will work with main thread
        println("      'Unconfined': I'm working in thread ${Thread.currentThread().name}")
    }
    jobs += launch(coroutineContext) { // context of the parent, runBlocking coroutine
        println("'coroutineContext': I'm working in thread ${Thread.currentThread().name}")
    }
    jobs += launch(CommonPool) { // will get dispatched to ForkJoinPool.commonPool (or equivalent)
        println("      'CommonPool': I'm working in thread ${Thread.currentThread().name}")
    }
    jobs += launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread
        println("          'newSTC': I'm working in thread ${Thread.currentThread().name}")
    }
    jobs.forEach { it.join() }
}

介紹了多個調(diào)度器

launch(Unconfined) 
launch(coroutineContext):這個調(diào)度器只有在runBlocking內(nèi)部才能用,嚴(yán)格來說不算調(diào)度器,內(nèi)部協(xié)程的下上文中,繼續(xù)啟動協(xié)程

launch(CommonPool) 
launch(newSingleThreadContext("MyOwnThread")) 

具體解釋看開篇的說明

fun main(args: Array<String>) = runBlocking<Unit> {
    val jobs = arrayListOf<Job>()
    jobs += launch(Unconfined) { // not confined -- will work with main thread
        println("      'Unconfined': I'm working in thread ${Thread.currentThread().name}")
        delay(500)
        println("      'Unconfined': After delay in thread ${Thread.currentThread().name}")
    }
    jobs += launch(coroutineContext) { // context of the parent, runBlocking coroutine
        println("'coroutineContext': I'm working in thread ${Thread.currentThread().name}")
        delay(1000)
        println("'coroutineContext': After delay in thread ${Thread.currentThread().name}")
    }
    jobs.forEach { it.join() }
}

println(" 'Unconfined': After delay in thread ${Thread.currentThread().name}")
這一句將會在新的協(xié)程中打印出來,因?yàn)閰f(xié)程本身被delay了

private val log = LoggerFactory.getLogger(X::class.java)

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")

fun main(args: Array<String>) = runBlocking<Unit> {
    val a = async(coroutineContext) {
        log.info("I'm computing a piece of the answer")
        log("I'm computing a piece of the answer")
        6
    }
    val b = async(coroutineContext) {
        log.info("I'm computing another piece of the answer")
        log("I'm computing a piece of the answer")
        7
    }
    log.info("The answer is ${a.await() * b.await()}")
}

這里要講的是日志:如果你是lombok的使用者,那么很遺憾,lombox現(xiàn)在暫不支持在kotlin使用@Slf4j或者@Log4j

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
這一句是官方示例給的,最好別用

private val log = LoggerFactory.getLogger(X::class.java)

跟以前一樣用LoggerFactory拿就好了

fun main(args: Array<String>) = runBlocking<Unit> {
    println("My job is ${coroutineContext.get(Job.Key)}")
    println("My job is ${coroutineContext.get(Job)}")
    println("My job is ${coroutineContext[Job]}")
}

runBlocking<Unit> 這個老伙計了,老伙計本身其實(shí)也是coroutines啟動的沒想到吧,驚不驚喜,意不意外。這種設(shè)計就跟golang一樣,有個統(tǒng)一的runtime管理器,但這里是顯式的。
它被設(shè)計出來最大的原因就是阻塞執(zhí)行了,在它內(nèi)部可以啟動多個async協(xié)程,然后共同計算出一個復(fù)雜的對象,然后統(tǒng)一返回給runBlocking,外部就可以直接接收

maven配置
其實(shí)可以直接引用kotlinx-coroutines-core,不過它的依賴項(xiàng)會晚于官方的發(fā)布版本所以我們先排除它的依賴自己引用最新版的kotlin

kotlin-stdlib-jre8或者kotlin-stdlib-jre7
或者直接就用kotlin-stdlib都是可以的。

          <dependency>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-stdlib</artifactId>
                <version>1.1.3-2</version>
            </dependency>

       
          <!--  <dependency>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-stdlib-jre8</artifactId>
                <version>1.1.3-2</version>
            </dependency>-->

               <!--  <     <dependency>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-stdlib-jre7</artifactId>
                <version>1.1.3-2</version>
            </dependency>-->


            <dependency>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-test</artifactId>
                <version>1.1.3-2</version>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <artifactId>kotlin-stdlib</artifactId>
                        <groupId>org.jetbrains.kotlin</groupId>
                    </exclusion>
                </exclusions>
            </dependency>

            <dependency>
                <groupId>org.jetbrains.kotlinx</groupId>
                <artifactId>kotlinx-coroutines-core</artifactId>
                <version>0.17</version>
                <exclusions>
                    <exclusion>
                        <artifactId>kotlin-stdlib</artifactId>
                        <groupId>org.jetbrains.kotlin</groupId>
                    </exclusion>
                </exclusions>
            </dependency>



plugin

           <plugin>
                    <groupId>org.jetbrains.kotlin</groupId>
                    <artifactId>kotlin-maven-plugin</artifactId>
                    <version>1.1.3-2</version>
                    <executions>
                        <execution>
                            <id>compile</id>
                            <phase>compile</phase>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>test-compile</id>
                            <phase>test-compile</phase>
                            <goals>
                                <goal>test-compile</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>compile</id>
                            <phase>compile</phase>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>testCompile</id>
                            <phase>test-compile</phase>
                            <goals>
                                <goal>testCompile</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>

關(guān)于Thread.sleep,這個最好不要在使用了coroutines后繼續(xù)用了,
如果是你配合spring-retry這種線程sleep的框架更要注意,高并發(fā)的情況下如果線程sleep,可能會導(dǎo)致線程無法喚醒,整個應(yīng)用處理不了請求

今天就聊到這,delay一下。歡迎留言交流關(guān)于kotlin.coroutines的問題
kotlinx.coroutines

下一章,應(yīng)該是下周

轉(zhuǎn)載請聯(lián)系我本人授權(quán)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評論 2 380

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