天天看點

生産者/消費者模式實作

  生産者-消費者也有多種實作方式。

    (1)常見的就是synchronized結合wait+notify實作

    (2)用Lock類實作

    (3)使用BlockingQueue阻塞隊列實作

  一個線程向集合中添加元素,兩個線程從集合中删除元素,與之前等待/通知部落格的最後一個案例類似。

結果:

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] 添加元素->0,threadName->B

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] list.remove ->0, threadName->sub2

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] 添加元素->1,threadName->B

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] list.remove ->1, threadName->sub1

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] 添加元素->2,threadName->B

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] list.remove ->2, threadName->sub2

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] 添加元素->3,threadName->B

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] list.remove ->3, threadName->sub1

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] 添加元素->4,threadName->B

18:16:23 [cn.qlq.thread.seven.Demo1]-[INFO] list.remove ->4, threadName->sub2

  假死的現象其實就是進入waiting狀态。如果全部線程都進入waiting狀态,則程式就不再執行任何業務功能了,整個項目呈停止狀态。

  例如兩個生産者兩個消費者最後處于假死狀态的例子:

18:49:23 [cn.qlq.thread.seven.Demo2]-[INFO] 進入等待***,threadName->sub1

18:49:23 [cn.qlq.thread.seven.Demo2]-[INFO] 進入等待***,threadName->sub2

18:49:24 [cn.qlq.thread.seven.Demo2]-[INFO] 添加元素->0,threadName->add1

18:49:24 [cn.qlq.thread.seven.Demo2]-[INFO] 進入等待***,threadName->add1

18:49:24 [cn.qlq.thread.seven.Demo2]-[INFO] 退出等待***,threadName->sub1

18:49:24 [cn.qlq.thread.seven.Demo2]-[INFO] list.remove ->0, threadName->sub1

18:49:24 [cn.qlq.thread.seven.Demo2]-[INFO] 進入等待***,threadName->sub1

18:49:24 [cn.qlq.thread.seven.Demo2]-[INFO] 退出等待***,threadName->sub2

18:49:24 [cn.qlq.thread.seven.Demo2]-[INFO] 進入等待***,threadName->sub2

18:49:25 [cn.qlq.thread.seven.Demo2]-[INFO] 添加元素->add2,threadName->{}

18:49:25 [cn.qlq.thread.seven.Demo2]-[INFO] 退出等待***,threadName->add1

18:49:25 [cn.qlq.thread.seven.Demo2]-[INFO] 進入等待***,threadName->add1

18:49:25 [cn.qlq.thread.seven.Demo2]-[INFO] 進入等待***,threadName->add2

18:49:35 [cn.qlq.thread.seven.Demo2]-[INFO] sub1 state->WAITING

18:49:35 [cn.qlq.thread.seven.Demo2]-[INFO] sub2 state->WAITING

18:49:35 [cn.qlq.thread.seven.Demo2]-[INFO] add1 state->WAITING

18:49:35 [cn.qlq.thread.seven.Demo2]-[INFO] add2 state->WAITING

  

解釋一下上面的線程假死的原因:

  由于喚醒線程調用的是notify()喚醒單個線程,是以有可能喚醒的是同類的線程,也就是生産者喚醒的是生産者,消費者喚醒的是消費者。導緻最後四個線程都處于waiting狀态。

解決辦法:

  喚醒的時候采用notifyAll()喚醒所有的線程喚醒所有的線程,避免隻喚醒同類線程。

  為了避免上面的假死線下,喚醒的時候采用notifyAll()喚醒所有的線程喚醒所有的線程,避免隻喚醒同類線程。

  喚醒的時候也喚醒異類,這樣就不會出現假死的狀态了,程式會一直運作下去。

  附上一個多生産與一個消費者的例子。大體的思路是對資源加鎖,線上程中通過while(true)不停的調用資源的add()方法和remove()方法,兩個方法都是同步方法,需要同步是對目前對象加鎖。每個對象都有一個阻塞隊列,一個就緒隊列。

  在學習完wait/notify之後再次重寫下面代碼就清晰多了。

  

  生産者和消費者分别阻塞在兩個條件下面的例子

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 0

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 1

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 2

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con1 消費元素 0

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con1 消費元素 1

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con1 消費元素 2

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 3

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 4

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 5

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con1 消費元素 3

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con1 消費元素 4

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con2 消費元素 5

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 6

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 7

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 8

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con2 消費元素 6

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con2 消費元素 7

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con2 消費元素 8

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > pro1 生産元素 9

13:31:28 [cn.qlq.thread.eleven.Demo4]-[INFO] threadName - > con2 消費元素 9

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro1 生産元素 0

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro1 生産元素 1

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro1 生産元素 2

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con1 消費元素 0

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con1 消費元素 1

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con1 消費元素 2

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro1 生産元素 3

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro1 生産元素 4

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro2 生産元素 0

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con1 消費元素 3

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con1 消費元素 4

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con2 消費元素 0

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro2 生産元素 1

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro2 生産元素 2

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro2 生産元素 3

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con2 消費元素 1

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con2 消費元素 2

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con2 消費元素 3

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > pro2 生産元素 4

13:36:49 [cn.qlq.thread.eleven.Demo5]-[INFO] threadName - > con2 消費元素 4

  參考我的另一篇部落格:https://www.cnblogs.com/qlqwjy/p/10175201.html 

【當你用心寫完每一篇部落格之後,你會發現它比你用代碼實作功能更有成就感!】

繼續閱讀