Java多线程基础与使用详细篇(一)----线程创建与线程停止

前言

什么是JMM主内存和本地内存(了解)

Java作为高级语言,屏蔽了这些底层细节,用JMM定义了一套读写内存数据的规范,虽然我们不再需要关心一级缓存和二级缓存的问题,但是,JMM抽象了主内存和本地内存的概念。这里说的本地内存并不是真的是一块给每个线程分配的内存,而是JMM的一个抽象,是JMM的一个抽象,是二级缓存等的抽象。


image.png
一、多线程的内存模型(JMM)

Java多线程内存模型(JMM)对于刚入门的跟我一样学习多线程的小白先初步了解一下三大特性。想要了解更多可到网上搜一下学习。

1.1 原子性

一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

1.2 可见性

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够“立即”看得到修改的值。

1.3 有序性

主要是用于线程间的通讯,比如join、wait等操作。

1.4 认识多线程基础的核心知识

多线程基础的核心知识有实现多线程有几种方式、启动线程的正确方法、停着线程、线程的状态等等。

二、 实现多线程的方法有几种方式

对于网上一搜就会有许多实现多线程的方式,但对于官方的说法正确的就只有两种。

2.1 实现方式
方法一实现Runnable接口,重写run()函数

推荐:Runnable接口
(1). 从代码架构角度
(2). 新建线程的损耗
(3). Java 不支持双继承
通过源码查看,方法一最终调用target.run();

public class RunnableStyle  implements Runnable{

    public static void main(String args[]){

        Thread thread = new Thread(new RunnableStyle());
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("用Runnable實現線程..");
    }
}
方法二继承Thread类,重写run()函数,运行start()方法
public class ThreadStyle  extends Thread{
    @Override
    public void run() {
        System.out.println("Thread 方式創建線程");
    }

    public static void main(String args[]){
        new ThreadStyle().start();
    }
}
2.2 同时使用Runnable和Thread

执行第一个run的时候没什么问题,但最后执行到第二个run的时候并调用start()方法,重写了runThread线程。

public class BothRunnableAndThread {

    public static void main(String args[]){
       new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println(" i come Runnable ....");
           }
       }){
           @Override
           public void run() {
               System.out.println(" i come thread ....");
           }
       }.start();
    }
}

结果: i come thread ....
2.3 对于创建线程的方式
  1. 通常我們可以分為兩類,Oracle官方也是這麼說
  2. 準確的講,創建線程只有一種方式那就是構造Thread類,而實現線程的執行單元有兩種方式
    (1). 方法一:實現Runnbale接口的run方法,然後把Runnbale實例傳給Thread類
    (2). 方法二: 重寫Thread的run方法(繼承Thread類)
3. 典型错误观点

(1). 線程池創建線程也算是一種新建線程的方式
(2).通過Callable和FutureTask創建方式 ,也算是一種新建線程的方式
(3). 無返回值是實現runnbale接口,有返回值是实现callable接口,所以callable是新的实现线程的方式
(4).定时器
(5).匿名内部类
(6).Lambda表达式

2.4 線程池創建線程也算是一種新建線程的方式
public class ThreadPoolDemo {

    public static void main(String args[]){
        ExecutorService executorService =
                Executors.newCachedThreadPool();
        for (int i = 0 ; i< 1000 ; i++){
            executorService.submit(
              new Tarsk(){}
            );
        }
    }
}

class Tarsk implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name:"+Thread.currentThread().getName());
    }
}
2.5 定时器也是算是一种
public class DemoTimmerTask {
    public static void main(String args[]){
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("name:"+Thread.currentThread().getName());
            }
        }, 1000,1000 );
    }
}
2.6 匿名内部类
public class AnonymousInnerClassDemo {
    public static void main(String args[]){
        new Thread(){
            @Override
            public void run() {
                System.out.println("name"+Thread.currentThread().getName());
            }
        }.start();
        new Thread(new Runnable() {
            public void run() {
                System.out.println("name"+Thread.currentThread().getName());
            }
        }).start();
    }
}
2.7 Lambda表达式
public class Lamba {

    public static void main(String args[]){
        new Thread(()-> System.out.println("name:"+Thread.currentThread().getName())).start();
    }
}
三、启动线程的正确和错误的方法
3.1 start()方法和run()的比较

输出结果的:一个是main一个Thread-0,
main是调用runnable.run(),带来的效果有偏差
Thread-0是调用 new Thread(runnable).start();实现的效果没有用偏差
代码演示:

/**
 * @Desc:  对比start和run两种启动线程的方式
 */
public class StartAndRunMethod {

    public static void main(String args[]){
        Runnable runnable = () ->{
          System.out.println("name:"+Thread.currentThread().getName());
        };
        runnable.run();

        new Thread(runnable).start();
    }
}
结果:
name:main
name:Thread-0
3.2 start()方法原理阅读

1.start()方法含义
(1).启动新线程
(2).准备工作
(3).不能重复start();方法
代码演示执行两次start方法:

public class CantStartTwice {
      public static void main(String args[]){
          Thread thread = new Thread();
          thread.start();
          thread.start();
      }
}
结果:
Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:708)
    at threadcoreknowledge.startthread.CantStartTwice.main(CantStartTwice.java:46)

2.start()源码解析
Thread最初始是为0,步骤如下
(1).一开始 启动新线程检查线程状态,
进入Thread.start()

      if (threadStatus != 0)
                        throw new IllegalThreadStateException();

(2). 加入线程租

                    group.add(this);

(3). 调用star0()

 start0();
                            started = true;

(4). 查看Thread方法下完整的start()方法

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
3.3 run()方法原理阅读

(1).源码解析
Thread类的run()方法

   public void run() {
                        if (target != null) {
                        target.run();
                        }

(2). 两种情况
第一种:继承Thread类重写Thread run方法

public class ThreadStyle  extends Thread{
    @Override
    public void run() {
        System.out.println("Thread 方式創建線程");
    }

    public static void main(String args[]){
        new ThreadStyle().start();
    }
}

第二种:传入target对象

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
3.4 总结:启动线程常见问题

(1). 一个线程两次调用start()方法会出现一个情况 为什么
答:抛出一个异常,在start方法最开始的状态去检查,如果不符合规定就会抛出异常
线程的六个状态: NEW 、Runnbale 、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
(2). 既然start()方法会调用run方法,为什么我们会选择调用start()方法,而不是直接调用run方法呢
调用start方法才是真正开启一个线程,run只是普通的方法

四、停止线程的正确方法(重要、难点)

4.1 如何正确停着线程
原理介绍:使用interrupt来通知,而不是强制
4.2.1. try、catch 使用:interrupt
(1).如果while里面放 try/catch,会导致中断失效,一直执行下去,只能自己手动关闭程序

public class CantInterrupt {

    public static void main(String args[]) throws InterruptedException {
        Runnable runnable = () ->{
            int num = 0 ;
            while (num <= 1000){
                if (num % 100 == 0){
                    System.out.println(num+"是100的倍数");
                }
                num++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}
结果:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at threadcoreknowledge.stopthreads.CantInterrupt.lambda$main$0(CantInterrupt.java:20)
    at java.lang.Thread.run(Thread.java:748)
500是100的倍数
600是100的倍数

(2). 实际开发中的两种最佳实践
方式一:优先选择:传递中断
在catch了InterruptedExcetion之后的优先选择:在方法签名中抛出异常,那么在run()就会强知try、catch。

public class RightWayStopThreadInProd  implements Runnable{
    @Override
    public void run() {
        while (true && !Thread.currentThread().isInterrupted()){
            System.out.println("start");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                // 保存日志、停止程序
                System.out.println("保存日志操作");
                e.printStackTrace();
            }
        }
    }

    private void throwInMethod() throws InterruptedException {
            Thread.sleep(2000);
    }
    public static void main(String args[]) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

方式二:不想或无法传递:恢复中断
catch子语句中调用Thread。currentThread().interrupt()来恢复设置中断状态,
以便于在后续的执行中,依然能够检查到刚才发生了中断。

public class RightWayStopThreadProd2 implements Runnable{

    @Override
    public void run() {
        while (true){
            if (Thread.currentThread().isInterrupted()){
                System.out.println("Interrupted,程序运行结束");
                break;
            }
            System.out.println("go");
            reInterrupt();

            // 保存日志、停止程序
                System.out.println("保存日志");

            }
        }


    private void reInterrupt()  {
        try{
            Thread.sleep(2000);
        }catch (InterruptedException e){
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }

    }
    public static void main(String args[]) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}
结果:
go
保存日志
java.lang.InterruptedException: sleep interrupted
Interrupted,程序运行结束
    at java.lang.Thread.sleep(Native Method)

4.2.2.响应中断的的方法总列表
下列都是响应中断方法的一些方法

 *  1. ◆Object.wait(/ wait( long)/ wait( long, int)
 *  2. ◆Thread. sleep( long) /sleep(long,int)
 *  3. ◆Thread. join0/ join( long)/join(Jong, int)
 *  4. ◆java. util.concurrent. BlockingQueue.take0/put( E)
 *  5. ◆java. util.concurrent.locks.Lock.lockInterruptibly()
 *  6. ◆java. util. concurrent. CountDownLatch. await(
 *  7. ◆java. util. concurrent. CyclicBarrier. awaitO
 *  8. ◆java. util. concurrent. Exchanger, exchangeM)
 *  9. ◆java.nio.channels.InterruptibleChannel相关方法
 *  10. ◆java.nio.channels.Selector的相关方法

4.2.3. 正确停止带来的好处
1.正确的停止方法:interrupt
(1).通常线程会在什么情况下停着普通情况
run方法内没有sleepwait方法时,
使用isInterruptedinterrupt停止线程

public class RightWayStopThreadWithoutSleep implements Runnable{
    @Override
    public void run() {
        int num = 0 ;
        // 需要添加isInterrupted去标识中断
        while (!Thread.currentThread().isInterrupted() &&
                num <= Integer.MAX_VALUE / 2) {
            if (num % 1000  == 0 ){
                System.out.println(num+"是10000的倍数");
            }
            num++;
        }
        System.out.println("任务运行结束");
    }
    public static void main(String args[]){
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        try {
            Thread.sleep(1000);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
结果:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
.........

(2).线程可能被阻塞
带有sleep的中断线程的写法,由于调用Thread.sleep()阻塞过程中,再用interrupt也可以解决阻塞的状态

public class RightWayStopThreadSleep {

    public static void main(String args[]) throws InterruptedException {
        Runnable runnable = () ->{
            int num = 0 ;
            while ( num <=300 && !Thread.currentThread().isInterrupted()){
                if (num % 100 == 0){
                    System.out.println(num+"是100的倍数");
                }
                num ++;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(500);
        thread.interrupt();
    }
}
结果:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at threadcoreknowledge.stopthreads.RightWayStopThreadSleep.lambda$main$0(RightWayStopThreadSleep.java:20)
    at java.lang.Thread.run(Thread.java:748)

(3).如果线程在每次迭代后都阻塞
如果在执行过程中,每次循环都会调用sleep或者wait等方法,那么不需要每次迭代都检查是否中断。因为在sleep类的方法中会帮我们响应中断。

public class RightWayStopThreadWithSleepEveryLoop {

    public static void main(String args[]) throws InterruptedException {
        Runnable runnable = () ->{
            int num = 0 ;
            try {
            while ( num <=10000){
                if (num % 100 == 0){
                    System.out.println(num+"是100的倍数");
                }
                num ++;
                Thread.sleep(100);
            }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}
结果:
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at threadcoreknowledge.stopthreads.RightWayStopThreadSleep.lambda$main$0(RightWayStopThreadSleep.java:20)
    at java.lang.Thread.run(Thread.java:748)

4.3 停止线程的错误方法
4.3.1. 被弃用的stop,suspend和resume
错误的停止方法: 用stop ()来停止线程,会导致线程运行一班突然停止, 没办法完成一个基本单位的操作(一个连队),会造成脏数据(有的连队多领取少领取装备)。下拉操作会导致后面领取装备的时候停止,从而程序没有继续执行下去。

public class ErroStopThread  implements Runnable{
    @Override
    public void run() {
        //模拟指挥军队:一共有5个连队,每个连队100人,以连队为单位,发放武器弹药,
        // 叫到号的士兵前去领取
        for (int i = 0 ; i < 5; i++){
            System.out.println("连队"+i+"开始领取武器");
            for (int j = 0 ; j < 10 ; j++){
                System.out.println("j:"+j);
                try {
                    Thread.sleep(50);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            System.out.println("连队"+i+"已经领取完毕");
        }
    };
    public static void main(String args[]){
        Thread thread = new Thread(new ErroStopThread());
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();

        }
        thread.stop();
    }
}
结果:
连队1开始领取武器
j:0
j:1
j:2
j:3
j:4
j:5
j:6
j:7
j:8
j:9

4.3.2. 用volatile设置boolean标识位
volatile修饰的boolean它是可见性行的,对于多个线程都可共享的值。可有参考网上学习JMM,当然内存模型和内存结构是不一样。
下面的执行结果会直接等待5秒就结束了,表面看是去让程序中断是没有什么问题,但是这样的方法是有足够的健壮性,就是适应当前的程序而不适应另外的一个程序。
下面执行的是个没有问题的程序

public class WrongWayVolatile implements Runnable {
    private volatile boolean cancled = false;
    @Override
    public void run() {
         int num = 0;
        try {
         while(num <= 10000 && !cancled){
             if (num % 100 ==0) {
                 System.out.println(num+"是100的倍数。");
             }
             num++;
                 Thread.sleep(1);
             }
             }catch (InterruptedException e) {
            e.printStackTrace();
         }
    }

    public static void main(String args[]) throws InterruptedException {
        WrongWayVolatile wrongWayVolatile =
                new WrongWayVolatile();
        Thread thread = new Thread(wrongWayVolatile);
        thread.start();
        Thread.sleep(5000);
        wrongWayVolatile.cancled = true;
    }
}
结果:
0是100的倍数。
100是100的倍数。
200是100的倍数。
300是100的倍数。
400是100的倍数。
.........

4.3.3演示用volatile的局限,陷入阻塞时,volatile是无法中断线程的
此例中,生产者的生产速度很快,消费者消费速度慢
所以阻塞队列满了以后,生产者会阻塞,等待消费者进一步消费,但是在此程序中一直阻塞了

public class WrongWayVolatileCantStop {

    public static void main(String args[]) throws InterruptedException {
        ArrayBlockingQueue storage = new
                ArrayBlockingQueue(10);

        Producer producer = new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);

        //消费者
        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()){
            System.out.println(consumer.storage.take()+"被消费了");
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据了。");

        //一旦消费不需要更多数据了,我们应当让生产者停下来,但是实际情况
        producer.cancled = true;
        System.out.println(producer.cancled);
    }
}

class Producer implements Runnable{
    public volatile boolean cancled = false;
    BlockingQueue storage;

    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }
    @Override
    public void run() {
        int num = 0;
        try {
            while(num <= 10000 && !cancled){
                if (num % 100 ==0) {
                    storage.put(num);
                    System.out.println(num+"是100的倍数,被放到仓库中了。。");
                }
                num++;

            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("生产者结束运行");
        }
    }
}

4.3.4 演示用中断来修复上面的程序无尽等待的问题

public class WrongWayVolatileFixed {

public static void main(String args[])throws InterruptedException{
      WrongWayVolatileFixed  body =
              new WrongWayVolatileFixed();

       ArrayBlockingQueue storage = new
                ArrayBlockingQueue(10);

        Producer producer = body.new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);

        //消费者
        Consumer consumer = body.new Consumer(storage);
        while (consumer.needMoreNums()){
            System.out.println(consumer.storage.take()+"被消费了");
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据了。");
 
        producerThread.interrupt();
        producerThread.isInterrupted();
}
    class Producer implements Runnable{

        BlockingQueue storage;

        public Producer(BlockingQueue storage) {
            this.storage = storage;
        }
        @Override
        public void run() {
            int num = 0;
            try {
                while(num <= 10000 && !Thread.currentThread().isInterrupted()){
                    if (num % 100 ==0) {
                        storage.put(num);
                        System.out.println(num+"是100的倍数,被放到仓库中了。。");
                    }
                    num++;
                }
            }catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println("生产者结束运行");
            }
        }
    }


    class Consumer {
        BlockingQueue storage;

        public Consumer(BlockingQueue storage) {
            this.storage = storage;
        }

        public boolean needMoreNums(){
            if (Math.random() >0.95){
                return false;
            }
            return true;
        }
    }
}
结果:
2500被消费了
3500是100的倍数,被放到仓库中了。。
消费者不需要更多数据了。
生产者结束运行
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
    at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353)

4.4 停止线程相关重要函数的源码解析
4.4.1. interrupt 方法
判断是否已被中断相关方法
(1). static boolean interrupt

static boolean interrupt
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();

synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0();           // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}

(2). boolean isInterrupted

public static boolean interrupted() {
return currentThread().isInterrupted(true);
}

(3).Thread.interrupted的目的对象
注意:Thread.interrupted()方法的目标对象是 "当前线程",而不管本方法来自于哪个对象

public class RightWayInterrupted {

    public static void main(String[] args) throws InterruptedException {

        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                for (; ; ) {
                }
            }
        });

        // 启动线程
        threadOne.start();
        //设置中断标志
        threadOne.interrupt();
        //获取中断标志
        System.out.println("isInterrupted: " + threadOne.isInterrupted());
        //获取中断标志并重置  静态方法
        System.out.println("isInterrupted: " + threadOne.interrupted());
        //获取中断标志并重直 主线程也是false
        System.out.println("isInterrupted: " + Thread.interrupted());
        //获取中断标志 返回上面第一个中断,依然是true
        System.out.println("isInterrupted: " + threadOne.isInterrupted());
        threadOne.join();
        System.out.println("Main thread is over.");
    }
}
结果:
isInterrupted: true
isInterrupted: false
isInterrupted: false
isInterrupted: true

(4).查看当前线程的isInterrupted方法

public boolean isInterrupted() {
return isInterrupted(false);
}

4.4.2 停止线程-总结
(1). 原理:用Interrupt来请求
(2). 想停止线程,要请求方、被停止方、子方法被调用方相互配合 try{}catch{}
(3). 最后再说错误的方法:stop/suspend已废弃,volateile的boolean无法处理长时间阻塞的情况
(4). 如何处理不可中断的阻塞

5.总结
大致上就把Java 多线程的八大核心前面的三个学习了解,这是用看某学习视频总结而来的个人学习文章。希望也能让观看此篇文章的小伙伴能够更加清晰的了解到多线程的基础来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354