多線程--基礎

Java多線程

從本篇開始丙者,筆者開始了一個新的專題复斥,來說說Java多線程

在講解Java多線程之前械媒,我們來了解下進程和線程的概念D慷А!@哪侣集!

進程

進程的概念,是60年代初首先由麻省理工學院的MULTICS系統(tǒng)和IBM公司的CTSS/360系統(tǒng)引入的兰绣。

對于操作系統(tǒng)來說世分,進程是最核心的概念,操作系統(tǒng)實現(xiàn)并發(fā)的基礎缀辩。進程是一個動態(tài)的過程臭埋,存在生命周期踪央,可以申請和擁有系統(tǒng)資源,是一個程序的執(zhí)行過程瓢阴,是一個活動的實體畅蹂。

程序作為一種軟件資料長期存在,指令和數(shù)據(jù)的有序集合荣恐,其本身沒有任何運行的含義液斜,是一個靜態(tài)的概念。而進程是有一定生命期的叠穆,程序在處理機上的一次執(zhí)行過程少漆,它是一個動態(tài)的概念,也就是說:程序是永久的硼被,進程是暫時的示损。

簡單的理解:進程是正在運行程序的實例。

我們知道嚷硫,進程是一個實體检访,它擁有自己的內(nèi)存空間,包含了文本區(qū)域(代碼)仔掸、數(shù)據(jù)區(qū)域(變量信息)和堆棧信息(調(diào)用指令)脆贵。此外,程序是沒有生命周期的起暮,只有當處理器賦予程序生命時丹禀,它便成為了一個活動的實體,即成為了一個進程鞋怀。

在不同操作系統(tǒng)下,進程的圖形化展示:

windows下開啟的線程
linux下開啟的進程

線程

線程持搜,也被稱為輕量級進程(Lightweight Process密似,LWP),是程序執(zhí)行流的最小單元葫盼。一個標準的線程由線程ID残腌,當前指令指針(PC),寄存器集合和堆棧組成贫导。

當我們運行一個程序時抛猫,系統(tǒng)會為我們創(chuàng)建一個進程,在實際運行過程中孩灯,進程會創(chuàng)建一個個線程闺金,以來實現(xiàn)程序不同的功能。

通常在一個進程中會包含若干個線程峰档,它們可以利用進程所擁有的資源败匹,但是其本身并不擁有系統(tǒng)資源寨昙。

在我們常見的操作系統(tǒng)中,進程是資源分配的基本單位掀亩,而把線程是獨立運行和獨立調(diào)度的基本單位舔哪。直白點,就是說操作系統(tǒng)給進程分配系統(tǒng)內(nèi)存槽棍、CPU等核心資源捉蚤,而進程來實現(xiàn)程序種的功能。

由于線程比進程更小炼七,不占用系統(tǒng)資源缆巧,對線程的調(diào)度所付出的開銷要小得多,所以能更高效的提高系統(tǒng)中多個程序間并發(fā)程度特石,從而顯著提高系統(tǒng)資源的利用率和吞吐量盅蝗。

多線程

在一個進程中,同時運行多個線程來完成不同的工作姆蘸,就稱為多線程墩莫。

多線程的存在,是為了同時完成多項任務逞敷,提高資源使用效率狂秦。

在Java中,一個Java程序的啟動推捐,意味著虛擬機這個進程的啟動裂问,當我們執(zhí)行一個main()方法時,實際上啟動了一個叫做main-thread的線程牛柒,這個線程就來實現(xiàn)我們所需要的功能堪簿、邏輯。

接下來皮壁,我們就來介紹下在Java中椭更,多線程的實現(xiàn)。

線程創(chuàng)建

在Java中蛾魄,創(chuàng)建創(chuàng)建有兩種方法:

繼承 Thread 類創(chuàng)建線程虑瀑;

實現(xiàn) Runnable 接口類創(chuàng)建線程;

(1)繼承Thread類

public class ThreadTest1 extends Thread{

    @Override
    public void run() {
        System.out.println("新啟線程為:"+Thread.currentThread().getName());
    }

    public static void main(String[] agrs){
        ThreadTest1 threadTest11 = new ThreadTest1();
        ThreadTest1 threadTest12 = new ThreadTest1();
        threadTest11.start();
        threadTest12.start();
        System.out.println("main線程為:"+Thread.currentThread().getName());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

測試結(jié)果如下:

main線程為:main

新啟線程為:Thread-0

新啟線程為:Thread-1

繼承Thread類滴须,需要重寫Thread中的run()方法舌狗,在run()方法中實現(xiàn)具體邏輯。在main()方法中扔水,創(chuàng)建線程對象痛侍,調(diào)用start()方法來啟動線程,之后會執(zhí)行run()方法中的邏輯魔市。此外恋日,我們還可以通過調(diào)用Thread的getName()方法膀篮,來獲取到線程的名稱。

(2)實現(xiàn)Runnable接口

public class ThreadTest2 implements Runnable{

    @Override
    public void run() {
        System.out.println("新啟線程為:"+Thread.currentThread().toString());
    }

    public static void main(String[] agrs){
        for(int x=0;x<10;x++){
            new Thread(new ThreadTest2()).start();
        }
        System.out.println("main線程為:"+Thread.currentThread().toString());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

測試結(jié)果如下:

main線程為:Thread[main,5,main]
新啟線程為:Thread[Thread-0,5,main]
新啟線程為:Thread[Thread-1,5,main]
新啟線程為:Thread[Thread-2,5,main]
新啟線程為:Thread[Thread-3,5,main]
新啟線程為:Thread[Thread-4,5,main]
新啟線程為:Thread[Thread-6,5,main]
新啟線程為:Thread[Thread-9,5,main]
新啟線程為:Thread[Thread-5,5,main]
新啟線程為:Thread[Thread-8,5,main]
新啟線程為:Thread[Thread-7,5,main]

實現(xiàn)Runnable接口岂膳,需要實現(xiàn)接口中的run()方法誓竿。與繼承Thread不同的是,在創(chuàng)建線程對象時需要借助Thread的構(gòu)造方法谈截,再調(diào)用start()方法來完成啟動線程筷屡。

在run()方法中,我們調(diào)用了Thread的toString()方法簸喂,該方法返回結(jié)果包括:線程的名稱毙死,線程的優(yōu)先級,線程組的名稱喻鳄;

通常扼倘,我們都是使用實現(xiàn)Runnable接口的方式來完成線程的創(chuàng)建和啟動。對于繼承Thread來說除呵,該方式實現(xiàn)起來編碼更簡單再菊,在run()方法內(nèi)部即可調(diào)用Thread類的方法;而實現(xiàn)Runnable接口方式颜曾,則極大避免了Java單繼承的局限纠拔。

從上面的兩個例子中可以看出,無論是繼承Thread泛豪、還是實現(xiàn)Runnable接口的方式稠诲,本質(zhì)上來說都離不開Thread類。

下面诡曙,我們來具體看下Thread類中有哪些主要方法:

線程方法

方法 方法描述
public void start() 使該線程開始執(zhí)行臀叙;Java 虛擬機調(diào)用該線程的 run 方法
public void run() 虛擬機執(zhí)行線程調(diào)用的方法
public final void setName(String name) 改變線程名稱
public final void setPriority(int priority) 更改線程的優(yōu)先級
public final void setDaemon(boolean on) 將該線程標記為守護線程
public final void join() 當我們調(diào)用某個線程的這個方法時,這個方法會掛起調(diào)用線程价卤,直到被調(diào)用線程結(jié)束執(zhí)行匹耕,調(diào)用線程才會繼續(xù)執(zhí)行
public void interrupt() 中斷線程,給在執(zhí)行的線程一個中斷信號荠雕,并不是停止線程的運行
public final boolean isAlive() 測試線程是否處于活動狀態(tài)
public static void yield() 暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程(也可能是自己)
public static void sleep(long millisec) 在指定的毫秒數(shù)內(nèi)讓當前正在執(zhí)行的線程休眠(暫停執(zhí)行
public static Thread currentThread() 返回對當前正在執(zhí)行的線程對象的引用

(1)調(diào)整線程優(yōu)先級--setPriority(int priority)

public class ThreadTest3 implements Runnable{
    @Override
    public void run() {
        System.out.println("新啟線程的優(yōu)先級為:"+Thread.currentThread().getPriority());
    }

    public static void main(String[] agrs){
        //設置main的線程優(yōu)先級:
        Thread.currentThread().setPriority(10);
        System.out.println("設置main線程的優(yōu)先級為:"+Thread.currentThread().getPriority());
        for(int x=0;x<10;x++){
            Thread thread = new Thread(new ThreadTest3());
            if(x%2==0){
                thread.setPriority(7);
            }
            thread.start();
        }
        System.out.println("main線程為:"+Thread.currentThread().toString());
    }
}

在上面的例子中驶赏,我們通過setPriority(int priority)來設置線程的優(yōu)先級炸卑,優(yōu)先級高的線程優(yōu)先執(zhí)行。

Java線程的優(yōu)先級取值范圍是1~~10煤傍,Thread類中有下面三個靜態(tài)常量:

static int MAX_PRIORITY:線程可以具有的最高優(yōu)先級盖文,取值為10
          
static int MIN_PRIORITY:線程可以具有的最低優(yōu)先級,取值為1
          
static int NORM_PRIORITY:分配給線程的默認優(yōu)先級蚯姆,取值為5

在Java線程中五续,每個線程都有默認的優(yōu)先級洒敏,默認為5.

此外,Java線程的優(yōu)先級還有繼承關系疙驾,例如上面的例子中凶伙,我們首先設置了main線程的優(yōu)先級,當我們在main中啟動別的線程時它碎,如果沒有對新啟動的線程指定優(yōu)先級函荣,那么新啟動的線程繼承main線程的優(yōu)先級。

(2)線程睡眠--sleep(long millisec)

public class ThreadTest4 implements Runnable{
    @Override
    public void run() {
        System.out.println("新啟線程:"+Thread.currentThread().toString());
        try {
            Thread.sleep(100);
            System.out.println("新啟線程停止500毫秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] agrs){
        for(int x=0;x<10;x++){
            Thread thread = new Thread(new ThreadTest4());
            thread.start();
        }
        System.out.println("main線程為:"+Thread.currentThread().toString());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

使正在運行的Java線程轉(zhuǎn)到阻塞狀態(tài)扳肛,millisec參數(shù)設定的是線程的睡眠時間傻挂,以毫秒為單位,sleep(long millisec)是靜態(tài)方法挖息,只能控制當前正在運行的線程金拒。

當Java線程在睡眠結(jié)束后,便會轉(zhuǎn)為就緒(Runnable)狀態(tài)套腹。

(關于線程的狀態(tài)绪抛,在下一小節(jié)介紹)

值得注意的是,一個線程執(zhí)行了sleep操作沉迹,如果這個線程獲取到鎖睦疫,那么sleep并不會讓出鎖。

前面說了鞭呕,一個線程在睡眠結(jié)束后蛤育,便會轉(zhuǎn)為就緒狀態(tài),并不會立刻執(zhí)行葫松,需要等待CPU的調(diào)度瓦糕,那么sleep中指定的時間就是線程休眠的最短時間。

(3)父線程等待子線程結(jié)束之后再運行--join()

public class ThreadTest5 implements Runnable{
    @Override
    public void run() {
        System.out.println("新啟線程:"+Thread.currentThread().toString());
    }

    public static void main(String[] agrs){
        List<Thread> list = new ArrayList<Thread>();
        for(int x=0;x<10;x++){
            Thread thread = new Thread(new ThreadTest5());
            list.add(thread);
            thread.start();
        }
        for(Thread thread:list){
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("main繼續(xù)做事腋么,main線程為:"+Thread.currentThread().toString());
    }
}

等待線程執(zhí)行完成后咕娄,再繼續(xù)執(zhí)行,這就是join存在的意義珊擂。當我們在線程A中圣勒,調(diào)用了線程B的join()方法,那么線程A會停下摧扇,被阻塞圣贸,但是不會釋放鎖(這一點跟sleep一樣),等待線程B執(zhí)行完成扛稽,此時線程A又恢復到了就緒狀態(tài)(不是立即執(zhí)行吁峻,這一點跟sleep也一樣)。

我們鎖了,join()會阻塞線程的執(zhí)行用含,那么為什么呢矮慕?我們來看看源碼:

//無參數(shù)的 join():
public final void join() throws InterruptedException {
    join(0);
}

public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    //如果等于0,則一直執(zhí)行while循環(huán)啄骇,循環(huán)體中調(diào)用wait()方法
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        //如果不為0痴鳄,則計算阻塞的時間:
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

通過源碼,我們發(fā)現(xiàn)肠缔,join內(nèi)部其實是由于wait()方法實現(xiàn)夏跷。當我們調(diào)用無參數(shù)的join()方法時,線程會一直執(zhí)行while循環(huán)明未,探測實現(xiàn)是否還存活槽华,存活就wait(0),就這樣一直在while循環(huán)中做判斷趟妥,當線程執(zhí)行結(jié)束后猫态,isAlive()返回false,while循環(huán)結(jié)束披摄。

(4)暫停當前正在執(zhí)行的線程亲雪,并執(zhí)行其他線程--yield()

public class ThreadTest8 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("新啟動線程:" + i);
            Thread.yield();
        }
    }

    public static void main(String[] agrs){
        new Thread(new ThreadTest8()).start();
        System.out.println("新線程啟動了");
        for (int i = 0; i < 10; i++) {
            System.out.println("main線程:" + i);
            Thread.yield();
        }
    }
}

暫停當前正在執(zhí)行的線程,執(zhí)行其他線程疚膊。對于其他線程义辕,這里面包含了兩種含義。

Thread.yield()執(zhí)行后寓盗,允許其他線程獲得運行機會灌砖。因此,使用yield()讓多個線程之間能適當?shù)妮嗈D(zhuǎn)執(zhí)行傀蚌。

但是基显,測試結(jié)果來看無法完全保證Thread.yield()的目的,執(zhí)行Thread.yield()的線程有可能被線程調(diào)度程序再次選中善炫,也就是說自己被認為了其他線程撩幽。

值得注意的是,Thread.yield()是將線程從運行狀態(tài)轉(zhuǎn)為了就緒狀態(tài)箩艺,并沒有阻塞線程窜醉。

(5)中斷線程--interrupt()

public class ThreadTest6 implements Runnable{
    @Override
    public void run() {
        while(true){
            if(Thread.currentThread().isInterrupted()){
                System.out.println("我被中斷了");
            }else{
                System.out.println("我一直在運行");
            }
        }
    }

    public static void main(String[] agrs){
        Thread thread = new Thread(new ThreadTest6());
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}

interrupt()方法通過修改了被調(diào)用線程的中斷狀態(tài)來告知那個線程, 告訴它自己已經(jīng)被中斷了,這里指的中斷是一個中斷信號艺谆,不是說將被調(diào)用的線程給干掉了榨惰,不要理解錯了。

我們可以通過調(diào)用線程的isInterrupted()方法擂涛,來獲取線程的中斷狀態(tài),通過此狀態(tài)來判斷線程中的執(zhí)行邏輯。

對于非阻塞線程來說(例如上面的例子)撒妈,調(diào)用interrupt()方法后恢暖,只是修改了線程的中斷狀態(tài),isInterrupted()返回true狰右。

但是對于阻塞線程來說杰捂,就不同了。

回想下棋蚌,當我們在程序中調(diào)用Thread.sleep()嫁佳、Object.wait()、Thread.join()時谷暮,會拋出一個叫InterruptedException的異常蒿往,看這個異常的命名,是不是跟現(xiàn)在我們所講的interrupt()方法類似湿弦。

沒錯瓤漏,對于阻塞線程來說,當我們執(zhí)行interrupt()方法后颊埃,被阻塞的線程會拋出InterruptedException異常蔬充,并且將線程中斷狀態(tài)置為true。至于班利,對異常的處理就因業(yè)務需求而已了饥漫。

public class ThreadTest6 implements Runnable{
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("終于明白sleep會拋出異常了捕獲異常了");
            }
        }
    }

    public static void main(String[] agrs){
        Thread thread = new Thread(new ThreadTest6());
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}

(6)設置守護線程--setDaemon()

public class ThreadTest7 implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("守護線程中,我會執(zhí)行嗎罗标?");
        }
    }

    public static void main(String[] agrs){
        Thread thread = new Thread(new ThreadTest7());
        thread.setDaemon(true);
        thread.start();
        System.out.println("守護線程啟動");
    }
}

在Java中庸队,線程分為兩大類型:用戶線程和守護線程。

通過Thread.setDaemon(false)設置為用戶線程馒稍,默認不調(diào)用此方法而創(chuàng)建的線程也是用戶線程皿哨;

通過Thread.setDaemon(true)設置為守護線程。

setDaemon()方法必須在start()方法之前設置纽谒,否則會拋出IllegalThreadStateException異常证膨。

守護線程和用戶線程有何不同?

當我們將一個線程設置為守護線程鼓黔,如果主線程執(zhí)行結(jié)束央勒,那么守護線程也會跟隨主線程一起結(jié)束。

而用戶線程卻不同澳化,如果主線程執(zhí)行結(jié)束崔步,但是用戶線程還在執(zhí)行,那么程序就不會停止缎谷。

最典型的守護線程井濒,就是JVM虛擬機中的垃圾回收器。

上面的例子中,新啟動的線程被設置成了守護線程瑞你,當main線程結(jié)束時酪惭,守護線程也隨之結(jié)束。但是者甲,守護線程中的finally代碼塊并不會執(zhí)行春感。

線程生命周期

線程是一個動態(tài)執(zhí)行的過程,它有一個從出生到死亡的過程虏缸。在Thread類中鲫懒,Java提供了線程的一生中會經(jīng)過哪些狀態(tài)。

(1)NEW:出生刽辙,new Thread()

(2)RUNNABLE:運行窥岩,start()、run()

(3)BLOCKED:阻塞扫倡,等待鎖synchronized block

(4)WAITING:無限等待谦秧,join()、wait()

(5)TIMED_WAITING:定時等待撵溃,sleep(x)巾兆、wait(x)州弟、join(x)

(6)TERMINATED:終結(jié),線程執(zhí)行完畢

列個表格,具體說下量瓜。需要注意的是跪呈,上面6種狀態(tài)與網(wǎng)上搜出來的很多文章并不一致(網(wǎng)上多一些狀態(tài)進行了歸類)妇汗,請以此為準孩革,因為這些狀態(tài)是Thread明確列出的。

方法 簡要說明
NEW 線程的初始狀態(tài)惶翻,也就是我們在代碼中new Thread()后的狀態(tài)姑蓝,還沒有調(diào)用start()方法
RUNNABLE 運行狀態(tài),這一點與網(wǎng)上的很多文章不一樣吕粗,在Thread類中纺荧,運行狀態(tài)包含了就緒和運行,也就是調(diào)用了start()和實際執(zhí)行run()
BLOCKED 阻塞狀態(tài)颅筋,線程在進入同步代碼塊之前宙暇,發(fā)現(xiàn)已經(jīng)有線程獲取到了鎖,所以本線程阻塞等待鎖的釋放
WAITING 等待狀態(tài)议泵,等待其他線程做一事情占贫,例如當我們的一個線程被執(zhí)行了Object.wait(),那么該線程實際在等待其他線程觸發(fā)Object.notify先口、Object.notifyAll()
TIMED_WAITING 定時等待型奥,與WAITING不同的是瞳收,此種狀態(tài)在達到一定時間便可返回運行狀態(tài),而不需要依賴其他線程處理厢汹,例如:Thread.sleep(x)缎讼、Object.wait(x)
TERMINATED 終結(jié)狀態(tài),也就是說線程執(zhí)行完畢了

下面坑匠,我們通過圖片再具體了解下,狀態(tài)之間的流轉(zhuǎn):

image

需要注意的是卧惜,圖中從其他狀態(tài)變?yōu)檫\行狀態(tài)時厘灼,其實是恢復成了就緒狀態(tài),還需要等待CPU的調(diào)度咽瓷,才能真正的執(zhí)行设凹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市茅姜,隨后出現(xiàn)的幾起案子闪朱,更是在濱河造成了極大的恐慌,老刑警劉巖钻洒,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奋姿,死亡現(xiàn)場離奇詭異,居然都是意外死亡素标,警方通過查閱死者的電腦和手機称诗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來头遭,“玉大人寓免,你說我怎么就攤上這事〖莆” “怎么了袜香?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲫惶。 經(jīng)常有香客問我蜈首,道長,這世上最難降的妖魔是什么剑按? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任疾就,我火速辦了婚禮,結(jié)果婚禮上艺蝴,老公的妹妹穿的比我還像新娘猬腰。我一直安慰自己,他們只是感情好猜敢,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布姑荷。 她就那樣靜靜地躺著盒延,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鼠冕。 梳的紋絲不亂的頭發(fā)上添寺,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音懈费,去河邊找鬼计露。 笑死,一個胖子當著我的面吹牛憎乙,可吹牛的內(nèi)容都是我干的票罐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼泞边,長吁一口氣:“原來是場噩夢啊……” “哼该押!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起阵谚,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蚕礼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后梢什,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奠蹬,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年嗡午,在試婚紗的時候發(fā)現(xiàn)自己被綠了罩润。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡翼馆,死狀恐怖割以,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情应媚,我是刑警寧澤严沥,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站中姜,受9級特大地震影響消玄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丢胚,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一翩瓜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧携龟,春花似錦兔跌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽华望。三九已至,卻和暖如春仅乓,著一層夾襖步出監(jiān)牢的瞬間赖舟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工夸楣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宾抓,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓豫喧,卻偏偏與公主長得像洞慎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嘿棘,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

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

  • 進程和線程 進程 所有運行中的任務通常對應一個進程,當一個程序進入內(nèi)存運行時,即變成一個進程.進程是處于運行過程中...
    小徐andorid閱讀 2,798評論 3 53
  • 為何星空這么璀璨 夜下,涼風吹過旭绒? 為何夕陽那么輝煌 黑夜將近鸟妙? 為何路那么長 走的人只一個? 為何看似那么近 實...
    黎埠閱讀 108評論 0 2
  • 與魔鬼戰(zhàn)斗的人,應當小心自己不要成為魔鬼忽匈。當你凝視深淵時房午,深淵也在凝視著你。 1914年第一次世界大戰(zhàn)前夕丹允,一名氣...
    阿棄閱讀 3,004評論 1 1
  • 今天在敲代碼的時候郭厌,出現(xiàn)一個cell復用以及GCD定時器倒計時的功能出現(xiàn)混亂的問題,由于之前一直使用NSTimer...
    漫步在銀河畔閱讀 1,635評論 0 1
  • 每天這個時候一塊黑巧雕蔽。 套路折柠,視之為無形∨看破不說破扇售。
    無隱閱讀 146評論 0 0