使用Lock来实现生产者和消费者问题

Lock的await/singal 和 Object的wait/notify 的区别

在使用Lock之前,我们都使用Object 的wait和notify实现同步的。举例来说,一个producer和consumer,consumer发现没有东西了,等待,produer生成东西了,唤醒。

线程consumer线程producer
synchronize(obj){
    obj.wait();//没东西了,等待
}
synchronize(obj){
    obj.notify();//有东西了,唤醒
}

有了lock后,世道变了,现在是:

lock.lock();
condition.await();
lock.unlock();
lock.lock();
condition.signal();
lock.unlock();

为了突出区别,省略了若干细节。区别有三点:

  1. 1. lock不再用synchronize把同步代码包装起来;
  2. 2. 阻塞需要另外一个对象condition;
  3. 3. 同步和唤醒的对象是condition而不是lock,对应的方法是await和signal,而不是wait和notify。

为什么需要使用condition呢?简单一句话,lock更灵活。以前的方式只能有一个等待队列,在实际应用时可能需要多个,比如读和写。为了这个灵活性,lock将同步互斥控制和等待队列分离开来,互斥保证在某个时刻只有一个线程访问临界区(lock自己完成),等待队列负责保存被阻塞的线程(condition完成)。

通过查看ReentrantLock的源代码发现,condition其实是等待队列的一个管理者,condition确保阻塞的对象按顺序被唤醒。

在Lock的实现中,LockSupport被用来实现线程状态的改变,后续将更进一步研究LockSupport的实现机制。


  package com.thread;
  
  import java.util.LinkedList;
  import java.util.concurrent.locks.Condition;
  import java.util.concurrent.locks.Lock;
  import java.util.concurrent.locks.ReentrantLock;
  
  
  /**
   * 使用Lock来实现生产者和消费者问题
   * 
   * 
   *
   */
  public class ProducerConsumer {
      public static void main(String[] args) {
          Basket b = new Basket();
          Product p = new Product(b);
          Consumer c = new Consumer(b);
          Consumer c1 = new Consumer(b);
          new Thread(p).start();
          new Thread(c).start();
          new Thread(c1).start();
      }
  }
  //馒头
  class ManTou{
      int id;
      public ManTou(int id) {
          this.id = id;
      }
      @Override
      public String toString() {
          return "ManTou"+id;
      }
  }
  
  //装馒头的篮子
  class Basket{
      int max = 6;
      LinkedList<ManTou> manTous = new LinkedList<ManTou>();
      Lock lock = new ReentrantLock(); //锁对象
      Condition full = lock.newCondition();  //用来监控篮子是否满的Condition实例
      Condition empty = lock.newCondition(); //用来监控篮子是否空的Condition实例
      //往篮子里面放馒头
      public void push(ManTou m){
          lock.lock();
          try {
              while(max == manTous.size()){
                  System.out.println("篮子是满的,待会儿再生产...");
                  full.await();
              }
              manTous.add(m);
              empty.signal();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }finally{
              lock.unlock();
          }
      }
      //往篮子里面取馒头
      public ManTou pop(){
          ManTou m = null;
          lock.lock();
          try {
              while(manTous.size() == 0){
                  System.out.println("篮子是空的,待会儿再吃...");
                  empty.await();
              }
              m = manTous.removeFirst();
              full.signal();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }finally{
              lock.unlock();
              return m;
          }
      }
  }
  //生产者
  class Product implements Runnable{
      Basket basket;
      public Product(Basket basket) {
          this.basket = basket;
      }
      public void run() {
          for (int i = 0; i < 40; i++) {
              ManTou m = new ManTou(i);
              basket.push(m);
              System.out.println("生产了"+m);
              try {
                  Thread.sleep((int)(Math.random()*2000));
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              
          }
      }
  }
 
 //消费者
 class Consumer implements Runnable{
     Basket basket;
     public Consumer(Basket basket) {
         this.basket = basket;
     }
     public void run() {
         for (int i = 0; i < 20; i++) {
             try {
                 Thread.sleep((int)(Math.random()*2000));
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             ManTou m = basket.pop();
             System.out.println("消费了"+m);
         }
     }
 }


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用Lock实现生产者队列和消费者队列相互唤醒沉睡的线程同步案例。 ```python import threading import time # 设置缓冲区大小 BUFFER_SIZE = 10 # 初始化锁 lock = threading.Lock() # 初始化条件变量 not_full = threading.Condition(lock) not_empty = threading.Condition(lock) # 初始化缓冲区 buffer = [] # 生产者线程 class Producer(threading.Thread): def run(self): global buffer while True: # 获得锁 lock.acquire() # 如果缓冲区已满,等待 while len(buffer) == BUFFER_SIZE: print("buffer is full, producer is waiting") not_full.wait() # 生产数据 buffer.append(1) print("produced, buffer size is ", len(buffer)) # 唤醒等待的消费者线程 not_empty.notify() # 释放锁 lock.release() # 等待一段时间再生产 time.sleep(1) # 消费者线程 class Consumer(threading.Thread): def run(self): global buffer while True: # 获得锁 lock.acquire() # 如果缓冲区为空,等待 while len(buffer) == 0: print("buffer is empty, consumer is waiting") not_empty.wait() # 消费数据 buffer.pop() print("consumed, buffer size is ", len(buffer)) # 唤醒等待的生产者线程 not_full.notify() # 释放锁 lock.release() # 等待一段时间再消费 time.sleep(2) # 启动生产者消费者线程 Producer().start() Consumer().start() ``` 在这个案例中,我们使用Lock锁和条件变量not_full、not_empty来实现了一个生产者队列和消费者队列相互唤醒沉睡的线程同步。具体的实现过程是: - 生产者线程每次生产一个数据时,先获得锁,如果缓冲区已满,则等待。如果缓冲区未满,则生产数据,将数据添加到缓冲区,输出缓冲区长度,并唤醒等待的消费者线程。最后释放锁并等待一定时间再继续生产。 - 消费者线程每次消费一个数据时,先获得锁,如果缓冲区为空,则等待。如果缓冲区不为空,则消费数据,将数据从缓冲区中删除,输出缓冲区长度,并唤醒等待的生产者线程。最后释放锁并等待一定时间再继续消费。 通过这个案例,我们可以看到Lock锁和条件变量的强大之处,它们可以帮助我们实现复杂的线程同步。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值