java lock await方法会释放掉当前锁 标准的生产者消费者问题

22 篇文章 0 订阅
12 篇文章 1 订阅

In all cases, before this method can return the current thread must

  • re-acquire the lock associated with this condition. When the

  • thread returns it is guaranteed to hold this lock.

    会释放,其他线程执行Condition.signal(),之前的线程会重新获得锁,继续执行,

    AbstractQueuedSynchronizer.java 第2040行,释放锁

  1.   //往篮子里面放馒头  
  2.      public void push(ManTou m){  
  3.          lock.lock();  
  4.          try {  
  5.              while(max == manTous.size()){  
  6.                  System.out.println("篮子是满的,待会儿再生产...");  
  7.                  full.await();  
  8.              }  
  9.              manTous.add(m);  
  10.              empty.signal();  
  11.          } catch (InterruptedException e) {  
  12.              e.printStackTrace();  
  13.          }finally{  
  14.              lock.unlock();  
  15.          }  
  16.      }  

 

  1. //往篮子里面取馒头  
  2.      public ManTou pop(){  
  3.          ManTou m = null;  
  4.          lock.lock();  
  5.          try {  
  6.              while(manTous.size() == 0){  
  7.                  System.out.println("篮子是空的,待会儿再吃...");  
  8.                  empty.await();  
  9.              }  
  10.              m = manTous.removeFirst();  
  11.              full.signal();  
  12.          } catch (InterruptedException e) {  
  13.              e.printStackTrace();  
  14.          }finally{  
  15.              lock.unlock();  
  16.              return m;  
  17.          }  
  18.      }  

 

对于上面例子push   full.await()执行后,

  1.  manTous.add(m);  
  2.              empty.signal(); 

 

不在执行,直接执行finally方法 释放掉锁,

不会影响其他线程执行remove之类的函数

对于其他线程增加push方法  也会进入await状态

 

当pop函数执行 后,集合可以再次push可以执行 ,full.signal(); 唤醒 await状态线程的push操作。

 

 

 

 

 

以下转载:

 

生产消费实例 参考

 

java 并发包下的提供Lock,Lock相对于Synchronized可以更好的解决线程同步问题,更加的灵活和高效,并且ReadWriteLock锁还能实现读、写的分离。但线程间仅仅互斥是不够的,还需要通信,本篇的内容是基于上篇之上,使用Lock如何处理线程通信。阻塞队列(BlockingQueue)就是使用condition的和lock实现的。可以查看:Java并发编程-阻塞队列(BlockingQueue)的实现原理

Condition 
那么引入本篇的主角,Condition,Condition 将 Object的通信方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 通信方法的使用。

在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。

Condition的强大之处在于它可以为多个线程间建立不同的Condition, 使用synchronized/wait()只有一个阻塞队列,notifyAll会唤起所有阻塞队列下的线程,而使用lock/condition,可以实现多个阻塞队列,signalAll只会唤起某个阻塞队列下的阻塞线程。

下面用两种方式编写生产者/消费者模式代码加以说明。

- 使用synchronized/wait()实现生产者消费者模式如下:

    //模拟生产和消费的对象
    class Buffer {
        private int maxSize;
        private List<Date> storage;
        Buffer(int size){
            maxSize=size;
            storage=new LinkedList<>();
        }
        //生产方法
        public synchronized void put()  {
            try {
                while (storage.size() ==maxSize ){//如果队列满了
                    System.out.print(Thread.currentThread().getName()+": wait \n");;
                    wait();//阻塞线程
                }
                storage.add(new Date());
                System.out.print(Thread.currentThread().getName()+": put:"+storage.size()+ "\n");
                Thread.sleep(1000);
                notifyAll();//唤起线程
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }       
        }
        //消费方法
        public synchronized void take() {
            try { 
                while (storage.size() ==0 ){//如果队列满了
                    System.out.print(Thread.currentThread().getName()+": wait \n");;
                    wait();//阻塞线程
                }
                Date d=((LinkedList<Date>)storage).poll();
                System.out.print(Thread.currentThread().getName()+": take:"+storage.size()+ "\n");
                Thread.sleep(1000);
                notifyAll();//唤起线程
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }       
        } 
}
//生产者
class Producer implements Runnable{
    private Buffer buffer;
    Producer(Buffer b){
        buffer=b;
    }
    @Override
    public void run() {
        while(true){
            buffer.put();
        }
    }   
}
//消费者
class Consumer implements Runnable{
    private Buffer buffer;
    Consumer(Buffer b){
        buffer=b;
    }
    @Override
    public void run() {
        while(true){
            buffer.take();
        }
    }   
}
//
public class Main{
    public static void main(String[] arg){
        Buffer buffer=new Buffer(10);
        Producer producer=new Producer(buffer);
        Consumer consumer=new Consumer(buffer);
        //创建线程执行生产和消费
        for(int i=0;i<3;i++){
            new Thread(producer,"producer-"+i).start();
        }
        for(int i=0;i<3;i++){
            new Thread(consumer,"consumer-"+i).start();
        }
    }
}

- 使用lock/condition实现生产者消费者模式如下:

import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Buffer {
    private  final Lock lock;
    private  final Condition notFull;
    private  final Condition notEmpty;
    private int maxSize;
    private List<Date> storage;
    Buffer(int size){
        //使用锁lock,并且创建两个condition,相当于两个阻塞队列
        lock=new ReentrantLock();
        notFull=lock.newCondition();
        notEmpty=lock.newCondition();
        maxSize=size;
        storage=new LinkedList<>();
    }
    public void put()  {
        lock.lock();
        try {   
            while (storage.size() ==maxSize ){//如果队列满了
                System.out.print(Thread.currentThread().getName()+": wait \n");;
                notFull.await();//阻塞生产线程
            }
            storage.add(new Date());
            System.out.print(Thread.currentThread().getName()+": put:"+storage.size()+ "\n");
            Thread.sleep(1000);         
            notEmpty.signalAll();//唤醒消费线程
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{   
            lock.unlock();
        }
    }

    public  void take() {       
        lock.lock();
        try {  
            while (storage.size() ==0 ){//如果队列满了
                System.out.print(Thread.currentThread().getName()+": wait \n");;
                notEmpty.await();//阻塞消费线程
            }
            Date d=((LinkedList<Date>)storage).poll();
            System.out.print(Thread.currentThread().getName()+": take:"+storage.size()+ "\n");
            Thread.sleep(1000);         
            notFull.signalAll();//唤醒生产线程

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    } 
}

class Producer implements Runnable{
    private Buffer buffer;
    Producer(Buffer b){
        buffer=b;
    }
    @Override
    public void run() {
        while(true){
            buffer.put();
        }
    }   
}
class Consumer implements Runnable{
    private Buffer buffer;
    Consumer(Buffer b){
        buffer=b;
    }
    @Override
    public void run() {
        while(true){
            buffer.take();
        }
    }   
}
public class Main{
    public static void main(String[] arg){
        Buffer buffer=new Buffer(10);
        Producer producer=new Producer(buffer);
        Consumer consumer=new Consumer(buffer);
        for(int i=0;i<3;i++){
            new Thread(producer,"producer-"+i).start();
        }
        for(int i=0;i<3;i++){
            new Thread(consumer,"consumer-"+i).start();
        }
    }
}

- 当生产者执行put方法时,调用notEmpty.signalAll()只会唤醒notEmpty.await()下的消费者线程。 
- 当消费者执行塔克方法时,调用notFull.signalAll()只会唤醒notFull.await()下的消费者线程。

 

 

另一篇:

 

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的实现机制。

 

 

[java] view plain copy

  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.   *  
  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. }  

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值