使用Lock來實現生產者和消費者問題


前面寫過:synchronize來實現生產者和消費者問題

現在用Lock來實現它

  1 package com.thread;
  2 
  3 import java.util.LinkedList;
  4 import java.util.concurrent.locks.Condition;
  5 import java.util.concurrent.locks.Lock;
  6 import java.util.concurrent.locks.ReentrantLock;
  7 
  8 
  9 /**
 10  * 使用Lock來實現生產者和消費者問題
 11  * 
 12  * @author 劉玲
 13  *
 14  */
 15 public class ProducerConsumer {
 16     public static void main(String[] args) {
 17         Basket b = new Basket();
 18         Product p = new Product(b);
 19         Consumer c = new Consumer(b);
 20         Consumer c1 = new Consumer(b);
 21         new Thread(p).start();
 22         new Thread(c).start();
 23         new Thread(c1).start();
 24     }
 25 }
 26 //饅頭
 27 class ManTou{
 28     int id;
 29     public ManTou(int id) {
 30         this.id = id;
 31     }
 32     @Override
 33     public String toString() {
 34         return "ManTou"+id;
 35     }
 36 }
 37 
 38 //裝饅頭的籃子
 39 class Basket{
 40     int max = 6;
 41     LinkedList<ManTou> manTous = new LinkedList<ManTou>();
 42     Lock lock = new ReentrantLock(); //鎖對象
 43     Condition full = lock.newCondition();  //用來監控籃子是否滿的Condition實例
 44     Condition empty = lock.newCondition(); //用來監控籃子是否空的Condition實例
 45     //往籃子里面放饅頭
 46     public void push(ManTou m){
 47         lock.lock();
 48         try {
 49             while(max == manTous.size()){
 50                 System.out.println("籃子是滿的,待會兒再生產...");
 51                 full.await();
 52             }
 53             manTous.add(m);
 54             empty.signal();
 55         } catch (InterruptedException e) {
 56             e.printStackTrace();
 57         }finally{
 58             lock.unlock();
 59         }
 60     }
 61     //往籃子里面取饅頭
 62     public ManTou pop(){
 63         ManTou m = null;
 64         lock.lock();
 65         try {
 66             while(manTous.size() == 0){
 67                 System.out.println("籃子是空的,待會兒再吃...");
 68                 empty.await();
 69             }
 70             m = manTous.removeFirst();
 71             full.signal();
 72         } catch (InterruptedException e) {
 73             e.printStackTrace();
 74         }finally{
 75             lock.unlock();
 76             return m;
 77         }
 78     }
 79 }
 80 //生產者
 81 class Product implements Runnable{
 82     Basket basket;
 83     public Product(Basket basket) {
 84         this.basket = basket;
 85     }
 86     public void run() {
 87         for (int i = 0; i < 40; i++) {
 88             ManTou m = new ManTou(i);
 89             basket.push(m);
 90             System.out.println("生產了"+m);
 91             try {
 92                 Thread.sleep((int)(Math.random()*2000));
 93             } catch (InterruptedException e) {
 94                 e.printStackTrace();
 95             }
 96             
 97         }
 98     }
 99 }
100 
101 //消費者
102 class Consumer implements Runnable{
103     Basket basket;
104     public Consumer(Basket basket) {
105         this.basket = basket;
106     }
107     public void run() {
108         for (int i = 0; i < 20; i++) {
109             try {
110                 Thread.sleep((int)(Math.random()*2000));
111             } catch (InterruptedException e) {
112                 e.printStackTrace();
113             }
114             ManTou m = basket.pop();
115             System.out.println("消費了"+m);
116         }
117     }
118 }

 附:synchronize與Lock的區別

一、synchronized和lock的用法區別
 
synchronized:在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括號中表示需要鎖的對象。
 
lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做為鎖,多個線程中必須要使用一個ReentrantLock類做為對象才能保證鎖的生效。且在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。
 
用法區別比較簡單,這里不贅述了,如果不懂的可以看看Java基本語法。
 
二、synchronized和lock性能區別
 
synchronized是托管給JVM執行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因為這是一個重量級操作,需要調用操作接口,導致有可能加鎖消耗的系統時間比加鎖以外的操作還多。相比之下使用Java提供的Lock對象,性能更高一些。但是到了Java1.6,發生了變化。synchronize在語義上很清晰,可以進行很多優化,有適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。導致在Java1.6上synchronize的性能並不比Lock差。官方也表示,他們也更支持synchronize,在未來的版本中還有優化余地。
 
說到這里,還是想提一下這2中機制的具體區別。據我所知,synchronized原始采用的是CPU悲觀鎖機制,即線程獲得的是獨占鎖。獨占鎖意味着其他線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉換線程阻塞時會引起線程上下文切換,當有很多線程競爭鎖的時候,會引起CPU頻繁的上下文切換導致效率很低。
 
而Lock用的是樂觀鎖方式。所謂樂觀鎖就是,每次不加鎖而是假設沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。樂觀鎖實現的機制就是CAS操作(Compare and Swap)。我們可以進一步研究ReentrantLock的源代碼,會發現其中比較重要的獲得鎖的一個方法是compareAndSetState。這里其實就是調用的CPU提供的特殊指令。
 
現代的CPU提供了指令,可以自動更新共享數據,而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。這個算法稱作非阻塞算法,意思是一個線程的失敗或者掛起不應該影響其他線程的失敗或掛起的算法。
 
我也只是了解到這一步,具體到CPU的算法如果感興趣的讀者還可以在查閱下,如果有更好的解釋也可以給我留言,我也學習下。
 
三、synchronized和lock用途區別
 
synchronized原語和ReentrantLock在一般情況下沒有什么區別,但是在非常復雜的同步應用中,請考慮使用ReentrantLock,特別是遇到下面2種需求的時候。
 
1.某個線程在等待一個鎖的控制權的這段時間需要中斷
2.需要分開處理一些wait-notify,ReentrantLock里面的Condition應用,能夠控制notify哪個線程
3.具有公平鎖功能,每個到來的線程都將排隊等候


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM