使用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 manTous = new LinkedList();
 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.具有公平锁功能,每个到来的线程都将排队等候

相关文章
|
7月前
|
安全 API C++
c++生产者和消费者线程循环
线程安全-生产者消费者模型
75 1
|
9月前
|
算法
生产者消费者问题(生产者和消费者分别阻塞于不同的锁)
生产者消费者问题(生产者和消费者分别阻塞于不同的锁)
|
9月前
|
算法
生产者消费者问题(生产者和消费者都阻塞于同一把锁this锁)
生产者消费者问题(生产者和消费者都阻塞于同一把锁this锁)
|
11月前
|
安全 Java
【JUC基础】06. 生产者和消费者问题
学习JUC,就不得不提生产者消费者。生产者消费者模型是一种经典的多线程模型,用于解决生产者和消费者之间的数据交换问题。在生产者消费者模型中,生产者生产数据放入共享的缓冲区中,消费者从缓冲区中取出数据进行消费。在这个过程中,生产者和消费者之间需要保持同步,以避免数据出现错误或重复。今天我们就来说说生产者消费者模型,以及JUC中如何解决该模型的同步问题。
127 0
|
安全 数据处理
线程中的生产者和消费者模式
线程中的生产者和消费者模式
100 0
线程中的生产者和消费者模式
一个简单的生产者和消费者客服实现
一个简单的生产者和消费者客服实现
116 0
|
Java
使用wait/notify实现生产者/消费者模式
上一篇文章:多线程编程之线程间通信机制:wait/notify机制重点讲了在java多线程 编程中协调线程间通信的wait/notify机制,它有力的保证了线程间通信的安全性以及便利性。这篇文章就来说说如何使用前面说到的wait/notify机制实现生产者/消费者模式。
213 0
使用wait/notify实现生产者/消费者模式
|
安全 Java
Java多线程——生产者/消费者问题
生产者/消费者问题
144 0

热门文章

最新文章