线程的通信
前面学习了用wait/notify的方式进行线程通信。今天学习一种更加强大的线程通信方式Condition.Condition的强大之处就是可以为线程建立不同的Condition。然后可以唤醒任意指定阻塞的线程。Condition之所以能为一个线程建立不同的Condition,是因为它也维护着一个阻塞的条件队列。它跟AQS等待队列通过线程的等待、唤醒建立关系。
1.每当AQS队列中的线程调用Condition.await()方法,AQS就会把当前线程从队列中移除,并加入到Condition队列的尾部并阻塞,然后等待唤醒。
2.每当AQS队列中线程对Condition队列中的某个阻塞线程进行了Condition.sinal()方法,Condition队列就会把该线程从队列中移除,线程加入AQS等待队列末尾,等待获取锁。
通过Condition实现多线程的消费者、生产者模式来进一步理解:
public class ConditionTest {
static class Depot{
private int size;
private ReentrantLock lock = null;
private Condition notEmptyCondition = null ;
private Condition notFullCondition = null;
private List<Integer> depots;
public Depot(int size) {
this.size = size;
lock = new ReentrantLock();
notEmptyCondition = lock.newCondition();
notFullCondition = lock.newCondition();
depots = new LinkedList<Integer>();
}
/**
* 每次生产count个产品
* @param count
*/
public void producer(int count) {
lock.lock();
try {
while(depots.size() > 0){
System.out.println("仓库已经有铲产品了,可以开始消费了");
notFullCondition.await(); //仓库满了进行等待消费
}
int actCount = (count + depots.size()) > size ? (size - depots.size()) : count; //如果生产的数量和现有的数量大于总数,就只生产仓库剩余的最大容量数
System.out.println(Thread.currentThread().getName() + "线程开始生产");
for(int i = 0; i < actCount; i++ ) {
depots.add(i);
}
System.out.println(Thread.currentThread().getName() + "生产完毕,当前仓库有" + depots.size() + "个产品");
notEmptyCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void consumer(int count) {
lock.lock();
try {
while(depots.size() == 0) {
System.out.println("当前仓库为空,等待生产");
notEmptyCondition.await();
}
int actCount = (depots.size() > count ) ? count : depots.size();//如果当前仓库剩余容量大于消费容量,就消费消费个,如果消费的容量比仓库剩余容量大,就把剩余的都消费掉。
System.out.println(Thread.currentThread().getName() + "线程开始消费");
Iterator<Integer> iterator = depots.iterator();
while(iterator.hasNext() && actCount > 0 ) {
iterator.next();
iterator.remove();
actCount--;
}
System.out.println(Thread.currentThread().getName() + "消费完毕,当前仓库有" + depots.size() + "个产品");
notFullCondition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
final Depot depot = new Depot(100);
for(int i = 0; i < 30; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
depot.producer(40);
}
}
},"producer-" + i );
thread.start();
}
for(int i = 0; i < 30; i++) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
while(true){
depot.consumer(80);
}
}
},"consumer-" + i);
thread1.start();
}
}
}
某一次运行部分结果
producer-20线程开始生产
producer-20生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-2线程开始消费
cusume-2消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-21线程开始生产
producer-21生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-28线程开始消费
cusume-28消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-15线程开始生产
producer-15生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-11线程开始消费
cusume-11消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-24线程开始生产
producer-24生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-14线程开始消费
cusume-14消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-19线程开始生产
producer-19生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-4线程开始消费
cusume-4消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-25线程开始生产
producer-25生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-15线程开始消费
cusume-15消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-8线程开始生产
producer-8生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-20线程开始消费
cusume-20消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-29线程开始生产
producer-29生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-5线程开始消费
cusume-5消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-1线程开始生产
producer-1生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-0线程开始消费
cusume-0消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-26线程开始生产
producer-26生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-18线程开始消费
cusume-18消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-16线程开始生产
producer-16生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-19线程开始消费
cusume-19消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-3线程开始生产
producer-3生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-12线程开始消费
cusume-12消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-7线程开始生产
producer-7生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-22线程开始消费
cusume-22消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-11线程开始生产
producer-11生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-26线程开始消费
cusume-26消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-2线程开始生产
producer-2生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-1线程开始消费
cusume-1消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-28线程开始生产
producer-28生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-23线程开始消费
cusume-23消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
producer-10线程开始生产
producer-10生产完毕,当前仓库有40个产品
仓库已经有铲产品了,可以开始消费了
cusume-27线程开始消费
cusume-27消费完毕,当前仓库有0个产品
当前仓库为空,等待生产
上面是简单模拟生产者消费者的例子。当然其中还有很多变化。还有就是用List来做存储。这个在多线程高并发环境下是不安全的等等。以后会学习JUC下的集合以及一些并发类。就能写出越来越安全的生产者消费真模式。