下面的代码有详细的注释,通过Condition的方式实现更细粒度的通知!
仔细阅读下面的代码,对多线程的理解有很大的帮助
package com.lyzx.concurrent.lock;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumer {
public static void main(String[] args) {
Box<String> box = new Box();
Runnable in = ()->{
for(int i=0;i<10;i++){
box.put("P"+i);
}
};
Runnable out = ()->{
for(int i=0;i<5;i++){
box.get();
}
};
for(int i=0;i<10;i++){
new Thread(out,"Consume_"+i).start();
if(i < 5){
new Thread(in,"Producer_"+i).start();
}
}
}
}
class Box<T>{
private Lock lock = new ReentrantLock();
private Queue<T> q = new LinkedList<>();
/**
* 这个Condition对象相当于一个Object对象,await方法让线程在此对象上等待
* 通过signalAll方法唤醒在这个对象上等待的所有线程
*/
// private Condition cond = lock.newCondition();
/**
* 通过ReentrantLock类的对象lock,获取一个"条件"
* 每次只让一种成员在这个对象上面等待
* 如所有的消费者都在 consumer 上等待,当生产者生产满了后直接调用 consumer.signalAll()
* 就可以唤醒所有的消费者了,不存在唤醒消费者让其再次等待的过程,所以理论上效率会更高,
*
* 记住Condition的await()方法会释放锁,此时它是持有锁的,所以才能释放锁,如果使用普通对象的
* wait方法时会抛出java.lang.IllegalMonitorStateException 这个异常
* 如下使用oConsumer和oProducer对象代替Con代替Condition的方法时会抛出异常
*
* 所以说通过Synchronized修饰方法或者使用Synchronized代码块是实现不了这样细粒度的通知的
*
*/
private Condition consumer = lock.newCondition();
private Condition producer = lock.newCondition();
// private Object oConsumer = new Object();
// private Object oProducer = new Object();
private final int MAX = 10;
private int size = 0;
/**
* 通过consumer和producer两个Condition即两个对象
*
* @return
*/
public T get(){
try{
lock.lock();
String c = Thread.currentThread().getName();
while(size == 0){
System.out.println(c+" get在等待!");
/**
* 1、等待的第一种形式,所有的成员(生产者和消费者)都在这个对象上等待
*/
//cond.await();
/**
* 2、等待的第二种形式
* 只让消费者线程在这个对象上等待,这个方法由消费者线程等待
* 当生产者线程生产满容器后就直接唤醒所有的消费者线程,不用唤醒生产者线程(相比于上一种形式)
* 理论上效率会高一些
*/
consumer.await();
// oConsumer.wait();
}
size--;
/**
* 1、唤醒的第一种形式,这里唤醒的是所有的成员,包括生产者和消费者
* 需要注意的是当唤醒消费者时(这个方法本身有消费者线程调用),等待的消费者会
* 做再一次的判断(通过while消除虚假唤醒),这次当然条件依然为true,所以会继续等待
*/
// cond.signalAll();
/**
* 2、第二种唤醒形式
* 这里只唤醒生产者进程,即在producer这个对象上等待的线程
* 理论上效率会比上一种形式高
*/
producer.signalAll();
// oProducer.notifyAll();
T t = q.poll();
System.out.println(c+" 取走了 "+t);
return t;
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
lock.unlock();
}
}
public void put(T t){
try{
lock.lock();
String c = Thread.currentThread().getName();
while(size == MAX){
System.out.println(c+" put在等待!");
// cond.await();
producer.await();
// oProducer.wait();
}
q.add(t);
System.out.println(Thread.currentThread().getName()+" 放入了 "+t);
size++;
// cond.signalAll();
consumer.signalAll();
// oConsumer.notifyAll();
}catch(Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}