Java多線程編程核心技術(shù)

本文主要基于 高洪巖《Java多線程編程核心技術(shù)》一書缴允,文章目錄基本與書保持一致米同,由于想偷懶就將看書過程中的md筆記拿來直接發(fā)布了翅楼,文章較長未做分割(預(yù)計等看完理解Java并發(fā)編程實(shí)踐一書后進(jìn)行拆分與整理)乎赴。同時也省略了很大一部分內(nèi)容忍法,重點(diǎn)主要在線程等待通知機(jī)制ReentrantLock的使用這兩大部分(BTW : 想要電子書的可以在文章下方留言)。

1.Java多線程基礎(chǔ)

1.1 進(jìn)程與多線程概念:

? 進(jìn)程:進(jìn)程是操作系統(tǒng)管理的基礎(chǔ)運(yùn)行單元

? 線程:線程為進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)

多線程通過異步執(zhí)行任務(wù)的方式榕吼,提高了系統(tǒng)的運(yùn)行效率

1.2 使用多線程

1.2.1 繼承Thread類

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

    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("myThread is running.....");
        }
    }

1.2.2 實(shí)現(xiàn)Runnable接口

public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread myThread = new Thread(myRunnable, "myThread");//具體參照Thread的構(gòu)造函數(shù)
        myThread.start();
    }


    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("Thread created by runnable interface");
        }
    }

上述兩種創(chuàng)建線程的方式可靈活選擇缔赠,如果要創(chuàng)建的線程類要繼承其它類的話,則采取實(shí)現(xiàn)Runnable接口的實(shí)現(xiàn)方式友题,因?yàn)镴ava不支持多繼承嗤堰,卻支持多實(shí)現(xiàn)。

1.2.3使用Callable和Future或FutureTask創(chuàng)建線程

1.2.3.1使用Callable和Future創(chuàng)建
public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        MyTask myTask = new MyTask();
        Future<Integer> submit = pool.submit(myTask);
        try {
            System.out.println(submit.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    static class MyTask implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            return 5;
        }
    }
1.2.3.2使用Callable和FutureTask創(chuàng)建
public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 5;
            }
        });
        pool.submit(task);
        try {
            System.out.println(task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

這種實(shí)現(xiàn)方式要配合線程池使用度宦。

1.2.4三種創(chuàng)建方式對比

  1. 使用繼承Thread就不可以繼承其他類
  2. 實(shí)現(xiàn)Callable接口的方式相比與實(shí)現(xiàn)Runable接口踢匣,可以獲取返回值同時可以拋出處理異常。(一般推薦采用實(shí)現(xiàn)接口的方式來創(chuàng)建多線程)

1.2.5 start()與run()方法區(qū)別

start()方法是線程的啟動方法戈抄,會開啟新線程离唬;run()方法不會開啟新線程,跟普通方法調(diào)用沒什么區(qū)別划鸽。

1.3.實(shí)例變量與線程安全

1.3.1 不共享數(shù)據(jù)

public static void main(String[] args) {
        MyThread a = new MyThread("A");
        MyThread b = new MyThread("B");
        MyThread c = new MyThread("C");
        MyThread d = new MyThread("D");
        MyThread e = new MyThread("E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }


    static class MyThread extends Thread{
        private int count = 5;

        public MyThread(String name){
            super(name);
        }

        @Override
        public void run() {
            --count;
            System.out.println(Thread.currentThread().getName()+"執(zhí)行: count = "+count);
        }
    }

/*****************************************執(zhí)行結(jié)果************************************************/
/**
A執(zhí)行: count = 4
B執(zhí)行: count = 4
D執(zhí)行: count = 4
C執(zhí)行: count = 4
E執(zhí)行: count = 4*/

1.3.2共享數(shù)據(jù)

public static void main(String[] args) {
        MyThread thread = new MyThread();
        Thread a = new Thread(thread,"A");
        Thread b = new Thread(thread,"B");
        Thread c = new Thread(thread,"C");
        Thread d = new Thread(thread,"D");
        Thread e = new Thread(thread,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }


    static class MyThread extends Thread{
        private int count = 5;

        public MyThread(){}

        public MyThread(String name){
            super(name);
        }

        @Override
        public void run() {
            count--;
            System.out.println(Thread.currentThread().getName()+"執(zhí)行: count = "+count);
        }
    }
/*****************************************運(yùn)行結(jié)果**********************************************/
/**
A執(zhí)行: count = 3
B執(zhí)行: count = 3
D執(zhí)行: count = 2
C執(zhí)行: count = 1
E執(zhí)行: count = 0
*/

從結(jié)果中可以看出A線程B線程的結(jié)果都是3,此處產(chǎn)生了線程安全的問題输莺,具體產(chǎn)生的原因可以查閱JVM中 --操作的原理戚哎,同時通過synchronized來實(shí)現(xiàn)線程安全,關(guān)于這方面的問題可以查看本人JVM相關(guān)模塊的文章嫂用。

1.4.停止線程

  1. 使用退出標(biāo)志型凳,使線程正常退出,也就是當(dāng)run方法完成后線程終止嘱函。(推薦)
  2. 使用stop方法強(qiáng)制終止線程甘畅,和suspend以及resume方法一樣,都是過期作廢的方法往弓,使用它們可能回產(chǎn)生不可預(yù)料的結(jié)果疏唾。(不推薦)
  3. 使用interrupt方法中斷線程。

1.5線程常用API(略)

2 對象及變量的并發(fā)訪問

2.1 Synchronized 同步方法

方法中的變量不存在線程安全的相關(guān)問題函似,線程安全問題只存在于被多個線程共享的實(shí)列變量中槐脏。使用Synchronized的修飾的方法為同步方法,能夠解決部分線程安全的問題撇寞。

2.2 靜態(tài)同步synchronized 方法與 synchronized (class) 代碼塊

  1. 如果synchronized 修飾的是static靜態(tài)方法顿天,那么就是對當(dāng)前文件所對應(yīng)的Class類進(jìn)行加鎖。
  2. sychronized修飾非靜態(tài)方法是給對象加鎖重抖。
  3. synchronized(class)代碼塊的作用和 synchronized static 方法的作用一樣露氮,都是給Class類進(jìn)行加鎖。

2.3 Synchronized 與String 類型常量池使用注意事項

將synchronized(String)同步塊與String聯(lián)合使用時钟沛,要注意一下常量池帶來的一些例外畔规。

public static void main(String[] args) {
        MyThread thread = new MyThread("AA");
//        MyThread thread = new MyThread(new Object());
        Thread a = new Thread(thread,"A");
        MyThread thread1 = new MyThread("AA");
//        MyThread thread1 = new MyThread(new Object());
        Thread b = new Thread(thread1,"B");

        a.start();
        b.start();

    }


    static class MyThread extends Thread{

        private Object objLock;

        public MyThread(Object objLock) {
            this.objLock = objLock;
        }

        @Override
        public void run() {
            try {
                synchronized (objLock){
                    while (true){
                        System.out.println(Thread.currentThread().getName()+"    is running!!!!! ");
                        Thread.sleep(1000);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
/*******************************************輸出結(jié)果**********************************************/
/**
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
A    is running!!!!! 
*/

當(dāng)使用字符串"AA"做鎖對象時,會發(fā)現(xiàn)同步塊會被線程獨(dú)占恨统,因此可以得知線程a和線程b持有的是同一把鎖叁扫。

2.4 synchronized 方法無限等待與解決

對不要求強(qiáng)一致性的方法采用不同的鎖對象或者采用同步塊與同步方法配合使用的方式來避免不必要的線程無線等待。

2.4.1 多線程的死鎖

不同的線程都在等待根本不可能被釋放的鎖畜埋,從而導(dǎo)致所有的任務(wù)都無法繼續(xù)完成莫绣,造成線程的“假死”狀態(tài)。

public static void main(String[] args) {
        DeadThread deadThread = new DeadThread();
        Thread deadOne = new Thread(deadThread, "a");
        Thread deadTwo = new Thread(deadThread, "b");
        deadOne.start();
        deadTwo.start();
    }


    static class DeadThread extends Thread{

        Object obj1 = new Object();
        Object obj2 = new Object();

        @Override
        public void run() {
            if("a".equals(currentThread().getName())){
                synchronized (obj1){
                    System.out.println(currentThread().getName()+"  locked obj1  waiting for obj2");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj2){
                        System.out.println(currentThread().getName() + " locked obj2");
                    }
                }
            }
            if ("b".equals(currentThread().getName())){
                synchronized (obj2){
                    System.out.println(currentThread().getName()+"  locked obj2  waiting for obj1");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj1){
                        System.out.println(currentThread().getName() + " locked obj1");
                    }
                }
            }
        }
    }

可以使用JDK自帶的監(jiān)測工具來檢測是否有死鎖現(xiàn)象發(fā)生悠鞍,進(jìn)入到JDK安裝的bin目錄对室,先執(zhí)行Jps命令查找出運(yùn)行線程的pid,然后通過jstack -l pid就可以查看線程死鎖相關(guān)信息了

2.4.2 死鎖的規(guī)避與解決

  1. 按順序加鎖:多個線程都按特定的加鎖順序來競爭鎖資源咖祭。
  2. 獲取鎖時限:每個線程獲取鎖的時候加上時限掩宜,如果超過某個時間就放棄獲取鎖。
  3. 死鎖檢測 :按線程獲取鎖的關(guān)系檢測線程間是否發(fā)生死鎖么翰,如果發(fā)生死鎖就執(zhí)行一定的策略牺汤,如中斷線程或回滾操作。

2. 5 volatile 關(guān)鍵字

volatile關(guān)鍵字的主要作用是保證變量操作的可見性浩嫌、有序性檐迟,但不保證其操作的原子性补胚。因此volatile關(guān)鍵字并不能真正的實(shí)現(xiàn)線程安全,只是線程同步的輕量級實(shí)現(xiàn)追迟。關(guān)鍵字volatile解決的是變量在多個線程之間的可見性溶其;而synchronized關(guān)鍵字解決的是多個線程之間訪問資源的同步性。

3. 線程間通信

3.1 等待/通知機(jī)制(wait/notify)

  1. wait() 方法可以使調(diào)用該方法的線程釋放共享資源的鎖怔匣,然后從運(yùn)行狀態(tài)退出握联,進(jìn)入等待隊列桦沉,直到被再次喚醒每瞒。

  2. notify() 方法可以隨機(jī)喚醒等待隊列中等待同一共享資源的"一個"線程,并使該線程退出等待隊列纯露,進(jìn)入可運(yùn)行狀態(tài)剿骨,也就是notify()方法僅通知"一個"線程。

  3. notifyAll() 方法可以使所有正在等待隊列中等待同一共享資源的"全部"線程從等待狀態(tài)退出埠褪,進(jìn)入可運(yùn)行狀態(tài)浓利。

    注意,wait()方法被執(zhí)行后钞速,鎖會被自動釋放贷掖,但執(zhí)行完notify()方法,鎖不自動釋放渴语;當(dāng)線程呈wait()狀態(tài)時苹威,調(diào)用線程對象的interrupt()方法會出現(xiàn) InterruptedException異常。

3.2生產(chǎn)者/消費(fèi)者模式的實(shí)現(xiàn)

3.3通過管道進(jìn)行線程間通信

3.4 ThreadLocal的使用

4. Lock的使用

4.1 ReentrantLock

4.1.1 使用 ReentrantLock 實(shí)現(xiàn)同步

public static void main(String[] args) {
        MyTask task = new MyTask();
        Thread t1 = new MyThread(task);
        Thread t2 = new MyThread(task);
        Thread t3 = new MyThread(task);
        Thread t4 = new MyThread(task);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

    static class MyTask{
        private Lock lock = new ReentrantLock();

        public void method(){
            //lock.lock();
            for (int i = 0 ; i < 5; i++){
                System.out.println("Thread name = "+Thread.currentThread().getName()+"   "+i);
            }
            //lock.unlock();
        }
    }

    static class MyThread extends Thread{

        private MyTask task;

        public MyThread(MyTask task) {
            this.task = task;
        }

        @Override
        public void run() {
            task.method();
        }
    }
/**************************************輸出結(jié)果***************************************************/
/**
Thread name = Thread-0   0
Thread name = Thread-1   0
Thread name = Thread-0   1
Thread name = Thread-1   1
Thread name = Thread-2   0
Thread name = Thread-0   2
Thread name = Thread-2   1
Thread name = Thread-1   2
Thread name = Thread-2   2
Thread name = Thread-0   3
Thread name = Thread-2   3
Thread name = Thread-1   3
Thread name = Thread-2   4
Thread name = Thread-0   4
Thread name = Thread-1   4
Thread name = Thread-3   0
Thread name = Thread-3   1
Thread name = Thread-3   2
Thread name = Thread-3   3
Thread name = Thread-3   4
此時驾凶,線程之間不是同步執(zhí)行的牙甫,將注釋處的代碼取消掉,就可以看見線程同步執(zhí)行的效果了
*/

4.1.2 使用Condition實(shí)現(xiàn)等待/通知

ReentrantLock借助Condition對象可以實(shí)現(xiàn)等待/通知模型调违,在一個Lock對象里面可以創(chuàng)建多個Condition實(shí)例窟哺,線程對象可以注冊在指定的Condition中,從而可以有選擇地進(jìn)行線程通知技肩,在調(diào)度線程上更加靈活且轨。

4.1.2.1 使用Condition實(shí)現(xiàn)線程一對一等待/通知
public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        WaitThread waitThread = new WaitThread(lock, condition);
        waitThread.setName("waitThread");
        NotifyThread notifyThread = new NotifyThread(lock, condition);
        notifyThread.setName("notifyThread");
        waitThread.start();
        notifyThread.start();
    }

    static class WaitThread extends Thread{

        private Lock lock;

        private Condition condition;

        public WaitThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                System.out.println("Thread : "+currentThread().getName()+" is waiting");
                condition.await();//注意是await()方法
                System.out.println("Thread "+currentThread().getName()+" : I'm wake up cause someone notify me ");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }


    static class NotifyThread extends Thread{

        private Lock lock;

        private Condition condition;

        public NotifyThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                Thread.currentThread().sleep(2000);
                System.out.println("Thread : "+currentThread().getName()+" call waiting thread");
                condition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
/*********************************************執(zhí)行結(jié)果*******************************************/
/**
Thread : waitThread is waiting
Thread : notifyThread call waiting thread
Thread waitThread : I'm wake up cause someone notified me
*/
4.1.2.2 使用多個Condition通知部分線程
public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition conditionA = lock.newCondition();
        Condition conditionB = lock.newCondition();
        WaitThread baseWaitA= new WaitThread(lock, conditionA);
        WaitThread baseWaitB= new WaitThread(lock, conditionB);
        Thread waitThread1 = new Thread(baseWaitA,"wait on Condition A T1");
        Thread waitThread2 = new Thread(baseWaitA,"wait on Condition A T2");
        Thread waitThread3 = new Thread(baseWaitB,"wait on Condition B T3");
        Thread waitThread4 = new Thread(baseWaitB,"wait on Condition B T4");

        NotifyThread baseNotify = new NotifyThread(lock, conditionB);
        Thread notifyBThread = new Thread(baseNotify,"i'll notify all Thread which waiting on Condition B");

        waitThread1.start();
        waitThread2.start();
        waitThread3.start();
        waitThread4.start();
        notifyBThread.start();
    }

    static class WaitThread extends Thread{

        private Lock lock;

        private Condition condition;

        public WaitThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                System.out.println("Thread : "+currentThread().getName()+" is waiting");
                condition.await();//注意是await()方法
                System.out.println("Thread "+currentThread().getName()+" : I'm wake up cause someone notified me ");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }


    static class NotifyThread extends Thread{

        private Lock lock;

        private Condition condition;

        public NotifyThread(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }


        @Override
        public void run() {
            try {
                lock.lock();
                Thread.currentThread().sleep(2000);
                System.out.println(currentThread().getName());
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
/*********************************************運(yùn)行結(jié)果********************************************/
/**
Thread : wait on Condition A T1 is waiting
Thread : wait on Condition A T2 is waiting
Thread : wait on Condition B T3 is waiting
Thread : wait on Condition B T4 is waiting
i'll notify all Thread which waiting on Condition B
Thread wait on Condition B T3 : I'm wake up cause someone notified me 
Thread wait on Condition B T4 : I'm wake up cause someone notified me
成功喚醒所有在Condition B上等待的線程
*/

4.1.3 ReentrantLock 實(shí)現(xiàn)生產(chǎn)者/消費(fèi)者模式

寫法1
public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition producerCondition = lock.newCondition();
        Condition consumerCondition = lock.newCondition();

        Producer producer = new Producer(lock, consumerCondition, producerCondition);
        Thread p1 = new Thread(producer, "P1");
        Thread p2 = new Thread(producer, "P2");
        Consumer consumer = new Consumer(lock, consumerCondition, producerCondition);
        Thread c1 = new Thread(consumer, "C1");
        Thread c2 = new Thread(consumer, "C2");
        Thread c3 = new Thread(consumer, "C3");

        p1.start();
        p2.start();
        c1.start();
        c2.start();
        c3.start();
    }


    static class Producer extends Thread{
        private Lock lock;

        private Condition consumerCondition;

        private Condition producerCondition;

        public Producer(Lock lock, Condition consumerCondition, Condition producerCondition) {
            this.lock = lock;
            this.consumerCondition = consumerCondition;
            this.producerCondition = producerCondition;
        }

        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();
                    Thread.sleep(500);
                    if (list.size() > 10){
                        consumerCondition.signalAll();
                        System.out.println(currentThread().getName()+" notified consumer and waiting");
                        producerCondition.await();
                    }else {
                        list.add(0);
                        System.out.println(currentThread().getName()+" 生產(chǎn)了一個產(chǎn)品  list size : "+list.size());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }


    static class Consumer extends Thread{

        private Lock lock;

        private Condition consumerCondition;

        private Condition producerCondition;

        public Consumer(Lock lock, Condition consumerCondition, Condition producerCondition) {
            this.lock = lock;
            this.consumerCondition = consumerCondition;
            this.producerCondition = producerCondition;
        }

        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();
                    if (list.size()<3){
                        System.out.println(currentThread().getName()+" is waiting list size : "+list.size());
                        producerCondition.signalAll();
                        consumerCondition.await();
                    }else {
                        Thread.sleep(1500);
                        list.remove(0);
                        System.out.println(currentThread().getName()+"  consumer one list size : "+list.size());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
寫法2
public static void main(String[] args) {
        Service service = new Service();
        ProducerThread producerThread = new ProducerThread(service);
        Thread p1 = new Thread(producerThread, "P1");
        Thread p2 = new Thread(producerThread, "P2");

        ConsumerThread consumerThread = new ConsumerThread(service);
        Thread c1 = new Thread(consumerThread, "c1");
        Thread c2 = new Thread(consumerThread, "c2");
        Thread c3 = new Thread(consumerThread, "c3");
        Thread c4 = new Thread(consumerThread, "c4");

        p1.start();
        p2.start();
        c1.start();
        c2.start();
        c3.start();
        c4.start();
    }


    static class ProducerThread extends Thread{
        private Service service;

        public ProducerThread(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.produce();
        }
    }

    static class ConsumerThread extends Thread{

        private Service service;

        public ConsumerThread(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.consumer();
        }
    }

    static class Service{
        private List<String> list = new ArrayList<>();
        Lock lock = new ReentrantLock();
        private Condition producerCondition = lock.newCondition();
        private Condition consumerCondition = lock.newCondition();

        public void produce(){
            while (true){
                try {
                    lock.lock();
                    Thread.sleep(500);
                    if (list.size() > 10){
                        consumerCondition.signalAll();
                        System.out.println(Thread.currentThread().getName()+" notified consumer and waiting");
                        producerCondition.await();
                    }
                    list.add("");
                    System.out.println(Thread.currentThread().getName()+" 生產(chǎn)了一個產(chǎn)品  list size : "+list.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }

        public void consumer(){
            while (true){
                try {
                    lock.lock();
                    if (list.size()<3){
                        System.out.println(Thread.currentThread().getName()+" is waiting list size : "+list.size());
                        producerCondition.signalAll();
                        consumerCondition.await();
                    }else {
                        Thread.sleep(1500);
                        list.remove(0);
                        System.out.println(Thread.currentThread().getName()+"  consumer one list size : "+list.size());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    }

4.1.4 公平鎖與非公平鎖

  • 公平鎖:線程獲取鎖的順序是按照線程加鎖的順序來分配的,即先來先得的FIFO先進(jìn)先出順序虚婿。

  • 非公平鎖:一種獲取鎖的搶占機(jī)制旋奢,是隨機(jī)獲得鎖的,先來的不一定能先得到鎖雳锋。

    public static void main(String[] args) {
            List<Thread> threads = new ArrayList<>();
            Myservice service = new Myservice(true);//使用公平鎖
          //Myservice service = new Myservice(false); 使用非公平鎖
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+" started ..... ");
                    service.invokeService();
                }
            };
    
            for (int i = 0 ; i < 10 ; i++){
                Thread thread = new Thread(runnable);
                threads.add(thread);
            }
    
            for (Thread t: threads){
                t.start();
            }
    
        }
    
        static class Myservice{
    
            private Lock lock;
    
            public Myservice(boolean isFair) {
                this.lock = new ReentrantLock(isFair);
            }
    
            public void invokeService(){
                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+"  獲得鎖定");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    /**********使用公平鎖時線程started輸出順序與 線程獲得鎖的輸出順序基本一致 反之則輸出則比較隨機(jī)**********/
    /**
    Thread-0 started ..... 
    Thread-1 started ..... 
    Thread-2 started ..... 
    Thread-0  獲得鎖定
    Thread-4 started ..... 
    Thread-3 started ..... 
    Thread-1  獲得鎖定
    Thread-2  獲得鎖定
    Thread-4  獲得鎖定
    Thread-3  獲得鎖定
    Thread-5 started ..... 
    Thread-5  獲得鎖定
    Thread-6 started ..... 
    Thread-6  獲得鎖定
    Thread-8 started ..... 
    Thread-8  獲得鎖定
    Thread-9 started ..... 
    Thread-9  獲得鎖定
    Thread-7 started ..... 
    Thread-7  獲得鎖定
    */
    /******************************************使用非公平鎖時**************************************/
    /**
    Thread-0 started ..... 
    Thread-1 started ..... 
    Thread-2 started ..... 
    Thread-0  獲得鎖定
    Thread-3 started ..... 
    Thread-3  獲得鎖定
    Thread-1  獲得鎖定
    Thread-2  獲得鎖定
    Thread-4 started ..... 
    Thread-5 started ..... 
    Thread-4  獲得鎖定
    Thread-5  獲得鎖定
    Thread-6 started ..... 
    Thread-6  獲得鎖定
    Thread-7 started ..... 
    Thread-8 started ..... 
    Thread-7  獲得鎖定
    Thread-9 started ..... 
    Thread-9  獲得鎖定
    Thread-8  獲得鎖定
    */
    

4.1.5 ReentrantLock類相關(guān)方法

4.1.5.1 方法getHoldCount() , getQueueLenght() , 和 getWaitQueueLength()
  1. getHoldCount() :查詢當(dāng)前線程保持此鎖定的個數(shù)黄绩,也就是調(diào)用lock()方法的次數(shù)。

    public static void main(String[] args) {
            Myservice myservice = new Myservice();
            GetHoldCountThread getHoldCountThread = new GetHoldCountThread(myservice);
            getHoldCountThread.setName("getHoldCountThread");
            getHoldCountThread.start();
        }
    
        static class Myservice{
            private Lock lock = new ReentrantLock();
    
            public void invoke(){
                try {
                    lock.lock();
                    System.out.println("第1次輸出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                    reInvoke();
                    Thread.sleep(2000);
                    System.out.println("第3次輸出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                    System.out.println("最后次輸出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                }
    
            }
    
            public void reInvoke(){
                try {
                    lock.lock();
                    Thread.sleep(2000);
                    System.out.println("第2次輸出 Thread "+Thread.currentThread().getName()+" getHoldCount : "+((ReentrantLock) lock).getHoldCount());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    
        static class GetHoldCountThread extends Thread{
    
            private Myservice myservice;
    
            public GetHoldCountThread(Myservice myservice) {
                this.myservice = myservice;
            }
    
            @Override
            public void run() {
                myservice.invoke();
            }
        }
    /*****************************************輸出結(jié)果********************************************/
    /**
    第1次輸出 Thread getHoldCountThread getHoldCount : 1
    第2次輸出 Thread getHoldCountThread getHoldCount : 2
    第3次輸出 Thread getHoldCountThread getHoldCount : 1
    最后次輸出 Thread getHoldCountThread getHoldCount : 0
    */
    
  2. getQueueLength()的作用是返回等待獲取此鎖的線程估計數(shù)玷过,比如有5個線程爽丹,1個線程首先執(zhí)行await()方法筑煮,那么在調(diào)用getQueueLength()方法后的返回值就是4,即還有四個線程在等待lock的釋放粤蝎。

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            List<Thread> threads = new ArrayList<>();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" 進(jìn)入方法 ");
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            for (int i = 0 ;i < 10 ; i++){
                Thread thread = new Thread(runnable);
                threads.add(thread);
            }
    
            for (int i = 0 ; i<threads.size(); i++){
                threads.get(i).start();
            }
    
            Thread.sleep(2000);
            System.out.println("還剩 "+((ReentrantLock) lock).getQueueLength()+" 線程在等待獲取鎖真仲!");
        }
    /*******************************************輸出結(jié)果***************************************/
    /**
    Thread-0 進(jìn)入方法 
    還剩 9 線程在等待獲取鎖!
    */
    
  3. getWaitQueueLength(Condition condition)的作用是返回在指定條件condition上等待的線程數(shù)初澎。比如有5個線程都在同一個condition上await()秸应,那么調(diào)用此方法時返回的值則為5。

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            Condition waitOnOdd = lock.newCondition();
            List<Thread> threads = new ArrayList<>();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
    
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" 進(jìn)入方法 ");
                        if ("1,3,5,9".contains(Thread.currentThread().getName())){
                            waitOnOdd.await();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            for (int i = 0 ;i < 10 ; i++){
                Thread thread = new Thread(runnable);
                thread.setName(""+i);
                threads.add(thread);
            }
    
            for (Thread t : threads){
                t.start();
            }
    
            Thread.sleep(2000);
            lock.lock();//注意碑宴,不在lock塊中調(diào)用getWaitQueueLength方法會拋出IllegalMonitorStateException
            System.out.println(((ReentrantLock) lock).getWaitQueueLength(waitOnOdd)+" 個線程 waiting on oddCondition");
            lock.unlock();
        }
    /*******************************************輸出********************************************/
    /**
    0 進(jìn)入方法 
    1 進(jìn)入方法 
    2 進(jìn)入方法 
    3 進(jìn)入方法 
    4 進(jìn)入方法 
    5 進(jìn)入方法 
    6 進(jìn)入方法 
    7 進(jìn)入方法 
    8 進(jìn)入方法 
    9 進(jìn)入方法 
    4 個線程 waiting on oddCondition
    */
    
4.1.5.2 hasQueuedThread()软啼,hasQueueThreds(),hasWaiters()方法
  1. hasQueuedThread(Thread thread)查詢指定的線程是否正在等待獲取鎖延柠。

  2. hasQueueThreads()查詢是否有線程正在等待獲取此鎖祸挪。

    public static void main(String[] args) throws Exception{
            ReentrantLock lock = new ReentrantLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" 進(jìn)入方法 ");
                        Thread.currentThread().sleep(Integer.MAX_VALUE);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(runnable);
            t1.setName("T1");
            t1.start();
    
            Thread.sleep(50);
    
            Thread t2 = new Thread(runnable);
            t2.start();
            Thread.sleep(500);
            System.out.println(lock.hasQueuedThread(t2));
            System.out.println(lock.hasQueuedThreads());
        }
    /******************************************輸出結(jié)果*******************************************/
    /**
    T1 進(jìn)入方法 
    true
    true
    */
    
  3. hasWaiters(Condition condition)的作用時查詢是否有線程正在等待與此鎖有關(guān)的condition條件。

    public static void main(String[] args) throws Exception{
            ReentrantLock lock = new ReentrantLock();
            Condition waitCondition = lock.newCondition();
            List<Thread> waits = new ArrayList<>();
            Runnable waitRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        waitCondition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Runnable notifyRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println("有沒有線程在waitCondition上等待 贞间? "+lock.hasWaiters(waitCondition)+" 等待線程數(shù): "+lock.getWaitQueueLength(waitCondition));
                        waitCondition.signal();
                        System.out.println("有沒有線程在waitCondition上等待 贿条? "+lock.hasWaiters(waitCondition)+" 等待線程數(shù): "+lock.getWaitQueueLength(waitCondition));
                        waitCondition.signalAll();
                        System.out.println("有沒有線程在waitCondition上等待 ? "+lock.hasWaiters(waitCondition)+" 等待線程數(shù): "+lock.getWaitQueueLength(waitCondition));
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            for (int i = 0 ; i < 10 ; i++){
                Thread thread = new Thread(waitRunnable);
                waits.add(thread);
            }
    
            for (Thread t : waits){
                t.start();
            }
    
            Thread.sleep(500);
            Thread notify = new Thread(notifyRunnable);
            notify.start();
        }
    /**********************************************輸出結(jié)果**************************************/
    /**
    有沒有線程在waitCondition上等待 增热? true 等待線程數(shù): 10
    有沒有線程在waitCondition上等待 整以? true 等待線程數(shù): 9
    有沒有線程在waitCondition上等待 ? false 等待線程數(shù): 0
    */
    
4.1.5.3 isFair()峻仇,isHeldByCurrentThread()公黑,isLocked()方法
  1. isFair() 判斷鎖是不是公平鎖

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock(true);
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    System.out.println("是否是公平鎖 : "+((ReentrantLock) lock).isFair());
                    lock.unlock();
                }
            }).start();
        }
    
  2. isHeldByCurrentThread() 的作用是查詢當(dāng)前線程是否保持此鎖

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("是否被當(dāng)前線程持有鎖 : "+((ReentrantLock) lock).isHeldByCurrentThread());
                    lock.lock();
                    System.out.println("是否被當(dāng)前線程持有鎖 : "+((ReentrantLock) lock).isHeldByCurrentThread());
                    lock.unlock();
                }
            }).start();
        }
    /**********************************************輸出結(jié)果**************************************/
    /**
    是否被當(dāng)前線程持有鎖 : false
    是否被當(dāng)前線程持有鎖 : true
    */
    
  3. isLocked() 查詢此鎖是否由任意線程持有

    public static void main(String[] args) throws Exception{
        Lock lock = new ReentrantLock();
    
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("是否被線程持有鎖 : "+((ReentrantLock) lock).isLocked());
                lock.lock();
                System.out.println("是否被線程持有鎖 : "+((ReentrantLock) lock).isLocked());
                lock.unlock();
            }
        }).start();
     }
    /**********************************************輸出結(jié)果**************************************/
    /**
    是否被當(dāng)前線程持有鎖 : false
    是否被當(dāng)前線程持有鎖 : true
    */
    
4.1.5.4 lockInterruptibly(),tryLock()和tryLock(long timeout础浮,TimeUnit unit) 方法
  1. lockInterruptibly()帆调,如果當(dāng)前線程未被中斷,則獲取鎖定豆同,如果已經(jīng)中斷則拋出異常

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
    //                    lock.lock();
                        lock.lockInterruptibly();
                        System.out.println(Thread.currentThread().getName()+" is running ..... ");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            });
            t.setName("T1");
            t.start();
            t.interrupt();
        }
    /**********************************************輸出結(jié)果**************************************/
    /**
    java.lang.InterruptedException
     at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
     at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
     at leonardo.ezio.springboot.demo2.Test.GetHoldCount$1.run(GetHoldCount.java:21)
     at java.lang.Thread.run(Thread.java:745)
    */
    
  2. tryLock()番刊,僅在調(diào)用時鎖未被另一線程占有的情況下,才獲取該鎖

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    boolean flag = lock.tryLock();
                    System.out.println(Thread.currentThread().getName()+" tryLock "+lock.tryLock());
                    if (flag){
                        System.out.println(Thread.currentThread().getName()+" 獲得鎖 ");
                    }else {
                        System.out.println(Thread.currentThread().getName()+" 沒有獲得鎖 ");
                    }
                }
            };
    
            Thread t1 = new Thread(runnable);
            Thread t2 = new Thread(runnable);
    
            t1.setName("T1");
            t2.setName("T2");
    
            t1.start();
            t2.start();
        }
    /*********************************************輸出結(jié)果*************************************/
    /**
    T2 tryLock false
    T2 沒有獲得鎖 
    T1 tryLock true
    T1 獲得鎖 
    */ 
    
  3. tryLock(long timeout影锈,TimeUnit unit) 芹务,如果鎖在給定等待時間內(nèi),沒有被另一個線程保持鸭廷,且當(dāng)前線程未被中斷枣抱,則獲取該鎖

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+" tryLock  currentTime : "+System.currentTimeMillis());
                        if (lock.tryLock(3,TimeUnit.SECONDS)){
                            System.out.println(Thread.currentThread().getName()+" 獲得鎖 currentTime : "+System.currentTimeMillis());
                        }else {
                            System.out.println(Thread.currentThread().getName()+" 沒有獲得鎖 currentTime : "+System.currentTimeMillis());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (((ReentrantLock) lock).isHeldByCurrentThread()){
                            System.out.println(Thread.currentThread().getName()+" is hold lock ");
                            if(Thread.currentThread().getName().equals("T1")){
                                try {
                                    Thread.sleep(3100);//T1睡眠若超過 給定的等待時間,則T2獲取不到鎖
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            lock.unlock();
                        }
                    }
                }
            };
    
            Thread t1 = new Thread(runnable);
            Thread t2 = new Thread(runnable);
    
            t1.setName("T1");
            t2.setName("T2");
    
            t1.start();
            Thread.sleep(50);
            t2.start();
        }
    /*********************************************輸出結(jié)果*************************************/
    /**
    T1 tryLock  currentTime : 1556248231940
    T1 獲得鎖 currentTime : 1556248231941
    T1 is hold lock 
    T2 tryLock  currentTime : 1556248231990
    T2 沒有獲得鎖 currentTime : 1556248234991 
    */ 
    
4.1.5.5 awaitUninterruptibly()辆床,awaitUntil() 方法
  1. awaitUninterruptibly()佳晶,線程await的時候被interrupt會拋出異常,若是awaitUninterruptibly則不會

    public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" begin waiting ");
                        condition.awaitUninterruptibly();
    //                    condition.await();
                        System.out.println(Thread.currentThread().getName()+" end waiting ");
    //                } catch (InterruptedException e) {
    //                    e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread thread = new Thread(runnable, "T1");
            thread.start();
            thread.interrupt();
        }
    
  2. awaitUntil()讼载,線程有限期等待轿秧,類似Object的wait(long millis)

     public static void main(String[] args) throws Exception{
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Calendar calendar = Calendar.getInstance();
                        calendar.add(Calendar.SECOND,10);
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+" begin waiting current time : "+System.currentTimeMillis());
                        condition.awaitUntil(calendar.getTime());
                        System.out.println(Thread.currentThread().getName()+" end waiting current time : "+System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread thread = new Thread(runnable, "T1");
            thread.start();
        }
    /*********************************************輸出結(jié)果*************************************/
    /**
    T1 begin waiting current time : 1556249594276
    T1 end waiting current time : 1556249604269
    */ 
    

    注:線程等待期間中跌,可以被其他線程喚醒。

4.1.6 ReentrantReadWriteLock

ReentrantReadWriteLock類中包含兩個鎖:一個是讀相關(guān)的鎖菇篡,也稱為共享鎖漩符;另一個是寫操作相關(guān)的鎖,也叫排他鎖驱还。多個讀鎖之間不互斥嗜暴,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥议蟆。

  1. 讀鎖共享

    public static void main(String[] args) throws Exception{
            ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.readLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get readLock "+System.currentTimeMillis());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.readLock().unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(runnable, "T1");
            Thread t2 = new Thread(runnable, "T2");
            t1.start();
            t2.start();
        }
    /*****************************************************************************************/
    /**
    T1 get readLock 1556259700048
    T2 get readLock 1556259700048
    */
    

    T1 和T2是同時獲得鎖的闷沥,因此讀鎖與讀鎖之間可以共享。

  1. 寫鎖互斥

    public static void main(String[] args) throws Exception{
            ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.writeLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get readLock "+System.currentTimeMillis());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.writeLock().unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(runnable, "T1");
            Thread t2 = new Thread(runnable, "T2");
            t1.start();
            t2.start();
        }
    /*************************************************************************************/
    /**
    T1 get readLock 1556260282635
    T2 get readLock 1556260283636
    */
    

    T1和T2存在競爭鎖的情況咪鲜,總有一個線程要等另一個線程釋放鎖之后才能嘗試獲取鎖

  1. 讀寫鎖互斥

    public static void main(String[] args) throws Exception{
            ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    
            Runnable writeRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.writeLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get writeLock "+System.currentTimeMillis());
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.writeLock().unlock();
                    }
                }
            };
    
            Runnable readRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        rwLock.readLock().lock();
                        System.out.println(Thread.currentThread().getName()+" get readLock "+System.currentTimeMillis());
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        rwLock.readLock().unlock();
                    }
                }
            };
    
            Thread t1 = new Thread(writeRunnable, "T1");
            Thread t2 = new Thread(readRunnable, "T2");
            t2.start();
            Thread.sleep(50);
            t1.start();
        }
    /********************************************************************************************/
    /**
    T2 get readLock 1556260986179
    */
    

    T1一直在等待T2釋放讀鎖

經(jīng)過以上講解狐赡,我們可以完全使用Lock對象來代替synchronized關(guān)鍵字撞鹉,同時Lock具有一些synchronized所不具備的功能疟丙,掌握Lock有助于理解并發(fā)包中源代碼的實(shí)現(xiàn)原理。

5 定時器Timer

定時器這里不做過多講述鸟雏,就提一下值得注意的兩點(diǎn)享郊。

  1. 當(dāng)任務(wù)執(zhí)行時間超過任務(wù)的循環(huán)間隔時,下一次任務(wù)開始的時間與正常情況下是不同的孝鹊。
  2. 任務(wù)的追趕性炊琉,如果我們設(shè)置任務(wù)開始的時間在當(dāng)前時間之前,那么他會計算出兩個時間的差又活,計算出時間差內(nèi)能夠執(zhí)行任務(wù)多少次苔咪,然后立即執(zhí)行。scheduleAtFixedRate具有追趕性柳骄,而Schedule不具有追趕性团赏。

6 單例模式與多線程(略)

7 線程組(略)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市耐薯,隨后出現(xiàn)的幾起案子舔清,更是在濱河造成了極大的恐慌,老刑警劉巖曲初,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件体谒,死亡現(xiàn)場離奇詭異,居然都是意外死亡臼婆,警方通過查閱死者的電腦和手機(jī)抒痒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颁褂,“玉大人故响,你說我怎么就攤上這事纷捞。” “怎么了被去?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵主儡,是天一觀的道長。 經(jīng)常有香客問我惨缆,道長糜值,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任坯墨,我火速辦了婚禮寂汇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捣染。我一直安慰自己骄瓣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布耍攘。 她就那樣靜靜地躺著榕栏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蕾各。 梳的紋絲不亂的頭發(fā)上扒磁,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音式曲,去河邊找鬼妨托。 笑死,一個胖子當(dāng)著我的面吹牛吝羞,可吹牛的內(nèi)容都是我干的兰伤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼钧排,長吁一口氣:“原來是場噩夢啊……” “哼敦腔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卖氨,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤会烙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筒捺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柏腻,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年系吭,在試婚紗的時候發(fā)現(xiàn)自己被綠了五嫂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖沃缘,靈堂內(nèi)的尸體忽然破棺而出躯枢,到底是詐尸還是另有隱情,我是刑警寧澤槐臀,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布锄蹂,位于F島的核電站,受9級特大地震影響水慨,放射性物質(zhì)發(fā)生泄漏得糜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一晰洒、第九天 我趴在偏房一處隱蔽的房頂上張望朝抖。 院中可真熱鬧,春花似錦谍珊、人聲如沸治宣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侮邀。三九已至,卻和暖如春布持,著一層夾襖步出監(jiān)牢的瞬間豌拙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工题暖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捉超。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓胧卤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拼岳。 傳聞我的和親對象是個殘疾皇子枝誊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355