多线程(三)协作篇之生产者消费者模型

使用多个线程除了并发进行提高一个大任务或者多个子任务的执行效率,多线程之间还存在协作完成任务,共同协作完成任务涉及到多线程之间的通讯。生产者消费者模型就是典型的多线程协作的应用


是什么:

什么是线程间的协作

  • 多线程协作即多线程之间通过多线程通讯技术,实现多线程按照特定顺序共同协作完成一项任务。

怎么用:

多线程协作用在哪

  • 多线程协作场景有多种使用场景,典型的有线程通过通知机制交替进行的生产者消费者模型:生产者生产物品,生产完通知消费者进行消费,消费者收到通知后进行消费,消费完再通知生产者进行生产;
  • 多个线程共同等待一齐完成后集体进行下一步,以此循环共同推进的循环路障模型
  • 某个线程任务等待多个线程都就绪后才开始的倒计时门闸模型

用什么实现生产者消费者模型

  • 使用objcet的自带的wait,notify,notifyAll方法进行线程间的通知等待机制,需要注意,这几个方法必须在synchronized的同步代码块的范围内使用,否则会抛异常。
public class ProduceConsumeDemo {
    public static void main(String[] args) {
        List<String> container=new ArrayList<>();
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        executorService.submit(new Consumer(container));
        executorService.submit(new Producer(container));
        executorService.shutdown();
    }
}
@Slf4j
class Consumer implements Runnable{
    int money=0;
    private List<String> container;
    public Consumer(List<String> container){
        this.container=container;
    }
    void consumeFood() throws Exception{
        while (money<15){
            synchronized (container){
                log.debug("container owner is consumer,money is {}",money);
                if(container.size()==0){
                    container.wait();
                }
                Iterator it=container.iterator();
                while(it.hasNext()){
                    money++;
                    log.debug("wow,the {} is delicious!nice food!",it.next());
                    it.remove();
                }
                log.debug(" container is empty,it is time to cook!");
                container.notifyAll();
                log.debug("consume  notified continue");
            }
        }
        log.debug("consumer money cost =15,over");
    }

    @Override
    public void run() {
        try {
            consumeFood();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

@Slf4j
class Producer implements Runnable{
    private String[] dishes={"rice","beef","chicken","tomato","potato"};
    int money=0;
    private Random random=new Random();
    private List<String> container;
    public Producer(List<String> container){
        this.container=container;
    }
    void produceFood() throws Exception{
        while (money<15){
            synchronized (container){
                log.debug("container owner is  produce,money is {}",money);
                if(container.size()>=5){
                    container.wait();
                }
                while(container.size()<5){
                    String dish=dishes[random.nextInt(5)];
                    log.debug("new food is cooked,it is {}",dish);
                    container.add(dish);
                    money++;
                }
                log.debug(" container is full,it is time to eat");
                // 做完后唤醒其他
                container.notifyAll();
                log.debug("produce  notified continue");
            }
        }
        log.debug("producer money cost =15,over");
    }

    @Override
    public void run() {
        try {
            produceFood();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印如下:
19:54:05.007 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - container owner is consumer,money is 0
19:54:05.011 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - container owner is  produce,money is 0
19:54:05.011 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is rice
19:54:05.011 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is beef
19:54:05.011 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is potato
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is potato
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is tomato
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer -  container is full,it is time to eat
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - produce  notified continue
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - container owner is  produce,money is 5
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the rice is delicious!nice food!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the beef is delicious!nice food!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the potato is delicious!nice food!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the potato is delicious!nice food!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the tomato is delicious!nice food!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer -  container is empty,it is time to cook!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - consume  notified continue
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - container owner is consumer,money is 5
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is tomato
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is chicken
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is chicken
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is tomato
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is rice
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer -  container is full,it is time to eat
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - produce  notified continue
19:54:05.012 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - container owner is  produce,money is 10
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the tomato is delicious!nice food!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the chicken is delicious!nice food!
19:54:05.012 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the chicken is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the tomato is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the rice is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer -  container is empty,it is time to cook!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - consume  notified continue
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - container owner is consumer,money is 10
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is tomato
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is beef
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is rice
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is tomato
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - new food is cooked,it is tomato
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer -  container is full,it is time to eat
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - produce  notified continue
19:54:05.013 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.Producer - producer money cost =15,over
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the tomato is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the beef is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the rice is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the tomato is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - wow,the tomato is delicious!nice food!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer -  container is empty,it is time to cook!
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - consume  notified continue
19:54:05.013 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.Consumer - consumer money cost =15,over
  • 使用并发队列BlockingQueue,BlockingQueue本身就是为生产者消费者模型设计的线程安全的并发容器,使用起来简单高效。
@Slf4j
public class ProduceConsumerQueueDemo {
    public static void main(String[] args) throws Exception{
        BlockingQueue<String> queue=new LinkedBlockingDeque<>(5);
        Random random=new Random();
        List<String> foods= Arrays.asList("beef","nice","meat","tomato","potato");
        ExecutorService executorService= Executors.newCachedThreadPool();
        CountDownLatch countDownLatch=new CountDownLatch(2);
        executorService.execute(()->{
            int i=0;
            String food;
            while(i<15){
                try {
                    food=queue.poll(10L, TimeUnit.SECONDS);
                    log.debug("delicious food,i like the {},size is {}",food,queue.size());
                    i++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            countDownLatch.countDown();
            log.debug("eat over!");
        });
        executorService.execute(()->{
            int i=0;
            String food;
            while(i<15){
                try {
                    food=foods.get(random.nextInt(5));
                    queue.offer(food,10L, TimeUnit.SECONDS);
                    log.debug("delicious food is cooked {},queue size is {}",food,queue.size());
                    i++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            countDownLatch.countDown();
            log.debug("cook over!");
        });
        countDownLatch.await();
        log.debug("main over!");
        executorService.shutdown();
    }
}
打印结果如下:
20:24:04.513 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked tomato,queue size is 1
20:24:04.513 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the tomato,size is 0
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the beef,size is 0
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked beef,queue size is 1
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked nice,queue size is 1
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the nice,size is 0
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked beef,queue size is 1
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked beef,queue size is 1
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the beef,size is 0
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked tomato,queue size is 2
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the beef,size is 1
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked beef,queue size is 2
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the tomato,size is 1
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked beef,queue size is 2
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the beef,size is 1
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked tomato,queue size is 2
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the beef,size is 1
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked nice,queue size is 2
20:24:04.516 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the tomato,size is 1
20:24:04.516 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked nice,queue size is 2
20:24:04.517 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked nice,queue size is 2
20:24:04.517 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the nice,size is 1
20:24:04.517 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked potato,queue size is 3
20:24:04.517 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the nice,size is 2
20:24:04.517 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked meat,queue size is 3
20:24:04.517 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the nice,size is 2
20:24:04.517 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food is cooked tomato,queue size is 3
20:24:04.517 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the potato,size is 2
20:24:04.517 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the meat,size is 1
20:24:04.517 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - cook over!
20:24:04.517 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - delicious food,i like the tomato,size is 0
20:24:04.517 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - eat over!
20:24:04.517 [main] DEBUG com.dz.demo.multiThread.ProduceConsumerQueueDemo - main over!
  • 使用LockSupport相关park与unpark方法,park方法使当前线程需要一个令牌才能继续(该令牌可以在park之前就被有或者park后被其他线程发放),unpark方法是给对应线程发放一个令牌。
public class ProduceConsumeLockSupportDemo {
    public static void main(String[] args) throws Exception{
        List<String>container=new ArrayList<>(5);
        Map<String,Thread> threadMap=new ConcurrentHashMap<>();
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        executorService.execute(new LockSupportProducer(threadMap,container));
        executorService.execute(new LockSupportConsumer(threadMap,container));
        executorService.shutdown();
    }
}

@Slf4j
class LockSupportProducer implements Runnable{
    private Map<String,Thread> threadMap;
    private List<String>container;
    public LockSupportProducer(Map<String,Thread> threadMap,List<String>container){
        this.threadMap=threadMap;
        this.container=container;
    }
    private  int money=0;
    private String[] dishes={"rice","beef","chicken","tomato","potato"};
    private Random random=new Random();
    @Override
    public void run() {
        threadMap.put("producer",Thread.currentThread());
        while(money<15){
            if(container.size()<5){
                String food=dishes[random.nextInt(5)];
                log.debug("cooking ,this time food is {},time is {}",food,money);
                container.add(food);
                money++;
            }else {
                LockSupport.unpark(threadMap.get("consumer"));
                LockSupport.park();
            }
        }
        LockSupport.unpark(threadMap.get("consumer"));
        log.debug("cooking over,time is {}",money);
    }
}

@Slf4j
class LockSupportConsumer implements Runnable{

    private Map<String,Thread> threadMap;
    private List<String>container;
    private  int money=0;
    public LockSupportConsumer(Map<String,Thread> threadMap,List<String>container){
        this.threadMap=threadMap;
        this.container=container;
    }
    @Override
    public void run() {
        threadMap.put("consumer",Thread.currentThread());
        while(money<15){
            if(container.size()>0){
                Iterator<String> it=container.iterator();
                while (it.hasNext()){
                    String food=it.next();
                    it.remove();
                    log.debug("eating ,delicious food is {},time is {}",food,money);
                    money++;
                }
            }else {
                LockSupport.unpark(threadMap.get("producer"));
                LockSupport.park();
            }
        }
        log.debug("eating over,time is {}",money);
    }
}
打印结果如下:
20:30:03.768 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is chicken,time is 0
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is rice,time is 1
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is beef,time is 2
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is beef,time is 3
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is potato,time is 4
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is chicken,time is 0
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is rice,time is 1
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is beef,time is 2
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is beef,time is 3
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is potato,time is 4
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is potato,time is 5
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is chicken,time is 6
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is potato,time is 7
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is potato,time is 8
20:30:03.772 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is chicken,time is 9
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is potato,time is 5
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is chicken,time is 6
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is potato,time is 7
20:30:03.772 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is potato,time is 8
20:30:03.773 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is chicken,time is 9
20:30:03.773 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is rice,time is 10
20:30:03.773 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is beef,time is 11
20:30:03.773 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is chicken,time is 12
20:30:03.773 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is beef,time is 13
20:30:03.773 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking ,this time food is potato,time is 14
20:30:03.773 [pool-1-thread-1] DEBUG com.dz.demo.multiThread.LockSupportProducer - cooking over,time is 15
20:30:03.773 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is rice,time is 10
20:30:03.773 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is beef,time is 11
20:30:03.773 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is chicken,time is 12
20:30:03.773 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is beef,time is 13
20:30:03.773 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating ,delicious food is potato,time is 14
20:30:03.773 [pool-1-thread-2] DEBUG com.dz.demo.multiThread.LockSupportConsumer - eating over,time is 15
  • 在分布式环境下的生产者消费者模型问题需要借助消息队列mq或者redis的list类型实现。这个后续文章讲到mq与redis会分享相关实践

本篇讲到了常见的三种多线程协作的模型,主要讲述了解决生产者消费者模型的三种常用办法,多线程协作的其他两个模型和相关api的使用将在下一篇中进行讲解

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

推荐阅读更多精彩内容