Carson帶你學(xué)Java:解決生產(chǎn)者加酵、消費(fèi)者問題的五種實(shí)現(xiàn)方式


1. 簡介

生產(chǎn)者 & 消費(fèi)者之間存在 強(qiáng)耦合問題


2. 解決方案

采用 生產(chǎn)者 & 消費(fèi)者 模式,具體介紹如下:

示意圖

3. 具體解決方式介紹

方式1:wait() / notify()

// Object類里的兩個(gè)方法,所有Object子類都可使用這2個(gè)方法
// 對(duì)象的監(jiān)視器對(duì)鎖對(duì)象的鎖定(也就是代碼中的lock對(duì)象),注意是調(diào)用鎖對(duì)象的wait() / nofity()
public class Test { 
    private static Integer count = 0; 
    private final Integer FULL = 5; 
    private static String lock = "lock"; 
 
    public static void main(String[] args) { 
        Test t = new Test(); 
        new Thread(t.new Producer()).start(); 
        new Thread(t.new Consumer()).start(); 
        new Thread(t.new Producer()).start(); 
        new Thread(t.new Consumer()).start(); 
    } 
 
    class Producer implements Runnable { 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e1) {  
                    e1.printStackTrace(); 
                } 
                synchronized (lock) { 
                    while (count == FULL) { 
                        try { 
                            lock.wait(); 
                        } catch (InterruptedException e) {  
                            e.printStackTrace(); 
                        } 
                    } 
                    count++; 
                    System.out.println("生產(chǎn)者"+Thread.currentThread().getName() 
                            + "已生產(chǎn)完成陌粹,商品數(shù)量:" + count); 
                    lock.notifyAll(); 
                } 
            } 
        } 
    } 
 
    class Consumer implements Runnable { 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e1) { 
                    e1.printStackTrace(); 
                } 
                synchronized (lock) { 
                    while (count == 0) { 
                        try { 
                            lock.wait(); 
                        } catch (InterruptedException e) { 
                            e.printStackTrace(); 
                        } 
                    } 
                    count--; 
                    System.out.println("消費(fèi)者"+Thread.currentThread().getName() 
                            + "已消費(fèi),剩余商品數(shù)量:" + count); 
                    lock.notifyAll(); 
                } 
            } 
        } 
    } 
}


// 測試結(jié)果

生產(chǎn)者Thread-0已生產(chǎn)完成福压,商品數(shù)量:1 
生產(chǎn)者Thread-2已生產(chǎn)完成掏秩,商品數(shù)量:2 
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:1 
消費(fèi)者Thread-3已消費(fèi)荆姆,剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成蒙幻,商品數(shù)量:1 
消費(fèi)者Thread-3已消費(fèi),剩余商品數(shù)量:0 
生產(chǎn)者Thread-2已生產(chǎn)完成胆筒,商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi)邮破,剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1 
生產(chǎn)者Thread-2已生產(chǎn)完成仆救,商品數(shù)量:2 
消費(fèi)者Thread-1已消費(fèi)抒和,剩余商品數(shù)量:1 
消費(fèi)者Thread-3已消費(fèi),剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成派桩,商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:0 
生產(chǎn)者Thread-2已生產(chǎn)完成蚌斩,商品數(shù)量:1 
消費(fèi)者Thread-3已消費(fèi)铆惑,剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi)送膳,剩余商品數(shù)量:0 
生產(chǎn)者Thread-2已生產(chǎn)完成员魏,商品數(shù)量:1 
消費(fèi)者Thread-3已消費(fèi),剩余商品數(shù)量:0

方式2:await() / signal()

// 對(duì)wait()/notify()的改進(jìn)叠聋,功能更加強(qiáng)大撕阎、更適用于高級(jí)用戶
// synchronized 托管給JVM執(zhí)行
// 而lock是Java寫的控制鎖的代碼

import java.util.concurrent.locks.Condition; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 
 
public class Test { 
    private static Integer count = 0;// 緩沖區(qū) 
    private final Integer FULL = 5; 
    final Lock lock = new ReentrantLock(); // 此處采用 ReentrantLock,獲得可重入鎖 
    final Condition put = lock.newCondition(); 
    final Condition get = lock.newCondition(); 
 
    public static void main(String[] args) { 
 
        Test t = new Test(); 
        new Thread(t.new Producer()).start(); 
        new Thread(t.new Consumer()).start(); 
        new Thread(t.new Consumer()).start(); 
        new Thread(t.new Producer()).start(); 
    } 
 
    // 生產(chǎn)者 
    class Producer implements Runnable { 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e1) {  
                    e1.printStackTrace(); 
                } 
                //加鎖 
                lock.lock(); 
                try { 
                    while (count == FULL) { 
                        try { 
                            put.await(); 
                        } catch (InterruptedException e) {  
                            e.printStackTrace(); 
                        } 
                    } 
                    count++; 
                    System.out.println("生產(chǎn)者" + Thread.currentThread().getName() 
                            + "已生產(chǎn)完成碌补,商品數(shù)量: " + count); 
                    //通知消費(fèi)者虏束,現(xiàn)在可以消費(fèi) 
                    get.signal(); 
                } finally { 
                    lock.unlock(); 
                } 
            } 
        } 
    } 
 
    // 消費(fèi)者 
    class Consumer implements Runnable { 
 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e1) {  
                    e1.printStackTrace(); 
                } 
                lock.lock(); 
                try { 
                    while (count == 0) { 
                        try { 
                            get.await(); 
                        } catch (Exception e) {  
                            e.printStackTrace(); 
                        } 
                    } 
                    count--; 
                    System.out.println("消費(fèi)者" + Thread.currentThread().getName() 
                            + "已消費(fèi),剩余商品數(shù)量: " + count); 
                    put.signal(); 
                } finally { 
                    lock.unlock(); 
                } 
            } 
        } 
    } 
}

// 測試結(jié)果
生產(chǎn)者Thread-3已生產(chǎn)完成厦章,商品數(shù)量: 1 
生產(chǎn)者Thread-0已生產(chǎn)完成镇匀,商品數(shù)量: 2 
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量: 1 
消費(fèi)者Thread-2已消費(fèi)袜啃,剩余商品數(shù)量: 0 
生產(chǎn)者Thread-3已生產(chǎn)完成汗侵,商品數(shù)量: 1 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量: 2 
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量: 1 
消費(fèi)者Thread-2已消費(fèi)晰韵,剩余商品數(shù)量: 0 
生產(chǎn)者Thread-0已生產(chǎn)完成发乔,商品數(shù)量: 1 
生產(chǎn)者Thread-3已生產(chǎn)完成,商品數(shù)量: 2 
消費(fèi)者Thread-1已消費(fèi)雪猪,剩余商品數(shù)量: 1 
消費(fèi)者Thread-2已消費(fèi)栏尚,剩余商品數(shù)量: 0 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量: 1 
生產(chǎn)者Thread-3已生產(chǎn)完成浪蹂,商品數(shù)量: 2 
消費(fèi)者Thread-2已消費(fèi)抵栈,剩余商品數(shù)量: 1 
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量: 0 
生產(chǎn)者Thread-3已生產(chǎn)完成坤次,商品數(shù)量: 1 
生產(chǎn)者Thread-0已生產(chǎn)完成古劲,商品數(shù)量: 2 
消費(fèi)者Thread-2已消費(fèi),剩余商品數(shù)量: 1 
消費(fèi)者Thread-1已消費(fèi)缰猴,剩余商品數(shù)量: 0

方式3:(BlockingQueue)阻塞隊(duì)列 系列方法

示意圖
// 下面主要使用其中的 put()产艾、take()
// put():將指定元素插入此隊(duì)列中,將等待可用的空間(若有必要)
// take():獲取并移除此隊(duì)列的頭部滑绒,在指定的等待時(shí)間前等待可用的元素(若有必要)

import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
 
public class Test { 
    private static Integer count = 0; 
    final BlockingQueue<Integer> bq = new ArrayBlockingQueue<Integer>(5);// 容量為5的阻塞隊(duì)列 
 
    public static void main(String[] args) { 
        Test t = new Test(); 
        new Thread(t.new Producer()).start(); 
        new Thread(t.new Consumer()).start(); 
        new Thread(t.new Consumer()).start(); 
        new Thread(t.new Producer()).start(); 
    } 
 
// 生產(chǎn)者
    class Producer implements Runnable { 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (Exception e) { 
                    e.printStackTrace(); 
                } 
                try { 
                    bq.put(1); 
                    count++; 
                    System.out.println("生產(chǎn)者" + Thread.currentThread().getName() 
                            + "已生產(chǎn)完成闷堡,商品數(shù)量:" + count); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
    } 
 
// 消費(fèi)者
    class Consumer implements Runnable { 
 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e1) { 
                    e1.printStackTrace(); 
                } 
                try { 
                    bq.take(); 
                    count--; 
                    System.out.println("消費(fèi)者" + Thread.currentThread().getName() 
                            + "已消費(fèi),剩余商品數(shù)量:" + count); 
                } catch (Exception e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
    } 
}

方式4:信號(hào)量 系列方法(Semaphore)

示意圖
// 實(shí)例使用
import java.util.concurrent.Semaphore; 
 
public class Test { 
    int count = 0; 
    final Semaphore put = new Semaphore(5);// 初始令牌個(gè)數(shù) 
    // 注:同步令牌(notFull.acquire())必須在互斥令牌(mutex.acquire())前面獲得疑故;若先得到互斥鎖再發(fā)生等待杠览,會(huì)造成死鎖。
    final Semaphore get = new Semaphore(0); 
    final Semaphore mutex = new Semaphore(1); 
 
    public static void main(String[] args) { 
        Test t = new Test(); 
        new Thread(t.new Producer()).start(); 
        new Thread(t.new Consumer()).start(); 
        new Thread(t.new Consumer()).start(); 
        new Thread(t.new Producer()).start(); 
    } 
 
    class Producer implements Runnable { 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (Exception e) { 
                    e.printStackTrace(); 
                } 
                try { 
                    put.acquire();// 注意順序 
                    mutex.acquire(); 
                    count++; 
                    System.out.println("生產(chǎn)者" + Thread.currentThread().getName() 
                            + "已生產(chǎn)完成纵势,商品數(shù)量:" + count); 
                } catch (Exception e) { 
                    e.printStackTrace(); 
                } finally { 
                    mutex.release(); 
                    get.release(); 
                } 
 
            } 
        } 
    } 
 
    class Consumer implements Runnable { 
 
        @Override 
        public void run() { 
            for (int i = 0; i < 5; i++) { 
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e1) { 
                    e1.printStackTrace(); 
                } 
                try { 
                    get.acquire();// 注意順序 
                    mutex.acquire(); 
                    count--; 
                    System.out.println("消費(fèi)者" + Thread.currentThread().getName() 
                            + "已消費(fèi)踱阿,剩余商品數(shù)量:" + count); 
                } catch (Exception e) { 
                    e.printStackTrace(); 
                } finally { 
                    mutex.release(); 
                    put.release(); 
                } 
            } 
        } 
    } 
}

// 測試結(jié)果
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1 
消費(fèi)者Thread-2已消費(fèi)钦铁,剩余商品數(shù)量:0 
生產(chǎn)者Thread-3已生產(chǎn)完成软舌,商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成牛曹,商品數(shù)量:1 
生產(chǎn)者Thread-3已生產(chǎn)完成佛点,商品數(shù)量:2 
消費(fèi)者Thread-2已消費(fèi),剩余商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi)黎比,剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成超营,商品數(shù)量:1 
生產(chǎn)者Thread-3已生產(chǎn)完成,商品數(shù)量:2 
消費(fèi)者Thread-2已消費(fèi)阅虫,剩余商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi)糟描,剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1 
生產(chǎn)者Thread-3已生產(chǎn)完成书妻,商品數(shù)量:2 
消費(fèi)者Thread-2已消費(fèi)船响,剩余商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi)躬拢,剩余商品數(shù)量:0 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1 
生產(chǎn)者Thread-3已生產(chǎn)完成见间,商品數(shù)量:2 
消費(fèi)者Thread-2已消費(fèi)聊闯,剩余商品數(shù)量:1 
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:0

方式5:PipedInputStream / PipedOutputStream

示意圖
import java.io.IOException; 
import java.io.PipedInputStream; 
import java.io.PipedOutputStream; 
 
public class Test { 
    final PipedInputStream pis = new PipedInputStream(); 
    final PipedOutputStream pos = new PipedOutputStream(); 
 
    public static void main(String[] args) { 
        Test t = new Test(); 
        new Thread(t.new Producer()).start(); 
        new Thread(t.new Consumer()).start(); 
    } 
 
    class Producer implements Runnable { 
 
        @Override 
        public void run() { 
            try { 
                pis.connect(pos); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
            try { 
                while (true) { // 不斷的產(chǎn)生數(shù)據(jù) 
                    int n = (int) (Math.random() * 255); 
                    System.out.println("生產(chǎn)者" + Thread.currentThread().getName() 
                            + "已生產(chǎn)完成米诉,商品數(shù)量:" + n); 
                    pos.write(n); 
                    pos.flush(); 
                } 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    pis.close(); 
                    pos.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
 
        } 
    } 
 
    class Consumer implements Runnable { 
 
        @Override 
        public void run() { 
            int n; 
            try { 
                while (true) { 
                    n = pis.read(); 
                    System.out.println("消費(fèi)者" + Thread.currentThread().getName() 
                            + "已消費(fèi)菱蔬,剩余商品數(shù)量:" + n); 
                } 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } finally { 
                try { 
                    pis.close(); 
                    pos.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
 
            } 
        } 
    } 
}

// 測試結(jié)果
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:6 
生產(chǎn)者Thread-0已生產(chǎn)完成史侣,商品數(shù)量:158 
生產(chǎn)者Thread-0已生產(chǎn)完成拴泌,商品數(shù)量:79 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:119 
生產(chǎn)者Thread-0已生產(chǎn)完成惊橱,商品數(shù)量:93 
生產(chǎn)者Thread-0已生產(chǎn)完成蚪腐,商品數(shù)量:213 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:151 
生產(chǎn)者Thread-0已生產(chǎn)完成税朴,商品數(shù)量:101 
生產(chǎn)者Thread-0已生產(chǎn)完成回季,商品數(shù)量:125 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:109 
生產(chǎn)者Thread-0已生產(chǎn)完成正林,商品數(shù)量:67 
生產(chǎn)者Thread-0已生產(chǎn)完成泡一,商品數(shù)量:109 
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:132 
生產(chǎn)者Thread-0已生產(chǎn)完成觅廓,商品數(shù)量:139 
... 



至此鼻忠,關(guān)于Java解決生產(chǎn)者、消費(fèi)者問題的五種實(shí)現(xiàn)方式講解完畢杈绸。


歡迎關(guān)注Carson_Ho的簡書帖蔓!

分享Android技術(shù)干貨,追求短蝇棉、平讨阻、快芥永,但卻不缺深度篡殷。


請點(diǎn)贊!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力埋涧!

相關(guān)文章閱讀
Android開發(fā):最全面板辽、最易懂的Android屏幕適配解決方案
Android事件分發(fā)機(jī)制詳解:史上最全面、最易懂
Android開發(fā):史上最全的Android消息推送解決方案
Android開發(fā):最全面棘催、最易懂的Webview詳解
Android開發(fā):JSON簡介及最全面解析方法!
Android四大組件:Service服務(wù)史上最全面解析
Android四大組件:BroadcastReceiver史上最全面解析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劲弦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子醇坝,更是在濱河造成了極大的恐慌邑跪,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異画畅,居然都是意外死亡砸琅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門轴踱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來症脂,“玉大人,你說我怎么就攤上這事淫僻∮张瘢” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵雳灵,是天一觀的道長棕所。 經(jīng)常有香客問我,道長细办,這世上最難降的妖魔是什么橙凳? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮笑撞,結(jié)果婚禮上岛啸,老公的妹妹穿的比我還像新娘。我一直安慰自己茴肥,他們只是感情好坚踩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓤狐,像睡著了一般瞬铸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上础锐,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天嗓节,我揣著相機(jī)與錄音,去河邊找鬼皆警。 笑死拦宣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的信姓。 我是一名探鬼主播鸵隧,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼意推!你這毒婦竟也來了豆瘫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤菊值,失蹤者是張志新(化名)和其女友劉穎外驱,沒想到半個(gè)月后育灸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昵宇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年描扯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趟薄。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绽诚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出杭煎,到底是詐尸還是另有隱情恩够,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布羡铲,位于F島的核電站蜂桶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏也切。R本人自食惡果不足惜扑媚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雷恃。 院中可真熱鬧疆股,春花似錦、人聲如沸倒槐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讨越。三九已至两残,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間把跨,已是汗流浹背人弓。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留着逐,地道東北人崔赌。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像滨嘱,于是被迫代替她去往敵國和親峰鄙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浸间,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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