生产者消费者模式之Lock与Condition

        synchronized中解决线程间通信的生产者消费者问题可以使用Object类的wait()notify()/notifyAll()方法(可参考,但是我在《同步之Lock锁中说过Lock是对synchronized的一种更为面向对象的替代,如果我们使用Lock来解决生产者消费者问题又将怎样编写代码呢?在JDK1.5中提供的Condition配套Lock可以实现相同的功能,Condition中的await()signal()/signalAll()就相当于Objectwait()notify()/notifyAll()。相信如果对Object的这三个方法了解的话对Condition的理解也就不难。

    不过要注意的是,Condition是被绑定到Lock上的,所以要创建一个LockCondition必须使用newCondition()方法。

        创建资源对象类Ticket


package com.gk.thread.communication;

public class Ticket {
	
	private String place;	// 车票的起始地
	private String date;	// 车票的日期
	private int number;		// 车票的数量
	public boolean empty = true;	// 标记,是否有车票,true表示没有车票
	
	// 省略一系列getXxx()和setXxx()...

}


    创建生产者类Producer


package com.gk.thread.communication.lock;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import com.gk.thread.communication.Ticket;

public class Producer implements Runnable{

	private Ticket ticket;
	private Lock lock;
	private Condition condition;
	
	public Producer(Ticket ticket, Lock lock, Condition condition) {

		this.ticket = ticket;
		this.lock = lock;
		this.condition = condition;
	}
	
	@Override
	public void run() {
		
		lock.lock();	// 加锁
		
		try {
			
			while(!ticket.isEmpty()) {
				condition.await();
			}
			
			String place = Thread.currentThread().getName();
			String date = new SimpleDateFormat("yyyy-MM-dd HH点mm分").format(new Date());
			int number = (int)(Math.random()*10 + 100);	
				
			/*
			 * 设置车票的属性
			 */
			ticket.setPlace(place);
			ticket.setDate(date);
			ticket.setNumber(number);
				
			System.out.println("生产了" + number + "张  " + date + "  " + place + "的票...\n");
			
			
			ticket.setEmpty(false);		// 生产者生产了票之后就有票了,所以修改标记empty为false
			
			condition.signalAll();		
			
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
			
		}finally {
			lock.unlock();		// 释放锁
		}
	}

}


扫描二维码关注公众号,回复: 2301818 查看本文章

    创建消费者类Customer


package com.gk.thread.communication.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import com.gk.thread.communication.Ticket;

public class Customer implements Runnable{
	
	private Ticket ticket;
	private Lock lock;
	private Condition condition;
	
	public Customer(Ticket ticket, Lock lock, Condition condition) {

		this.ticket = ticket;
		this.lock = lock;
		this.condition = condition;
	}

	@Override
	public void run() {
		
		lock.lock();
		try {
			
			while(ticket.isEmpty()) {
				condition.await();
			}
			
			String place = ticket.getPlace();
			String date = ticket.getDate();
			int number = ticket.getNumber();
				
			System.out.println("消费了" + number + "张  " + date + "  " + place + "的票...\n");
			
			ticket.setEmpty(true);		// 消费者消费了票之后就没有票了,所以修改标记empty为true

			condition.signalAll();
			
			
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}finally {
			lock.unlock();
		}
	}

}

    测试代码


package com.gk.thread.communication.lock;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.gk.thread.communication.Ticket;

public class Test {
	
	public static void main(String[] args) {
		
		Ticket ticket = new Ticket();
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		Runnable producer = new Producer(ticket, lock, condition);
		Runnable customer = new Customer(ticket, lock, condition);
		
		/*
		 * 多个生产者线程和多个消费者线程
		 */
		String from = "广州";
		String[] to = {"北京", "上海", "长沙", "杭州", "重庆", "西安", "厦门", "拉萨", "西藏", "哈尔滨"};
		List<String> place =  new ArrayList<String>();

		for(int i=0; i<to.length; i++) {
			
			place.add(from + "  --->  " + to[i]);
		}
		
		for(String p : place) {
			new Thread(producer, p).start();	// 生产者
			new Thread(customer).start();		// 消费者
		}
		
	}

}

    运行结果与《生产者消费者模式之synchronizedObject这篇博客是一致的,如果不理解,可参考《生产者消费者模式之synchronized与Object的解释。

 


    上面的例子可以看成是ConditionLock功能的补充(线程通信中synchronized对应Object的wait()notify()/notifyAll();Lock对应Conditionawait()signal()/signalAll()),但是Condition的强大之处在于它可以为多个线程间建立不同的Condition,也就是说Condition可以有多路等待和通知。我们知道synchronizednotifyAll()方法是唤醒所有等待的线程,如果有些线程不想唤醒呢?例如,在生产者类中我们就唤醒所有等待的消费者线程,在消费者类中就唤醒所有等待的生产者线程。请看下面代码。


    创建消费者类Producer


package com.gk.thread.communication.lock;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import com.gk.thread.communication.Ticket;

public class Producer2 implements Runnable {

	private Ticket ticket;
	private Lock lock;
	private Condition customerCondition;
	private Condition producerCondition;
	
	public Producer2(Ticket ticket, Lock lock, Condition customerCondition, Condition producerCondition) {
		this.ticket = ticket;
		this.lock = lock;
		this.customerCondition = customerCondition;
		this.producerCondition = producerCondition;
	}
	
	@Override
	public void run() {

		lock.lock();	// 加锁
		
		try {
			
			while(!ticket.isEmpty()) {
				producerCondition.await();		// 生产者线程等待
			}
			
			
			String place = Thread.currentThread().getName();
			String date = new SimpleDateFormat("yyyy-MM-dd HH点mm分").format(new Date());
			int number = (int)(Math.random()*10 + 100);	
				
			/*
			 * 设置车票的属性
			 */
			ticket.setPlace(place);
			ticket.setDate(date);
			ticket.setNumber(number);
				
			System.out.println("生产了" + number + "张  " + date + "  " + place + "的票...\n");
			ticket.setEmpty(false);	
			
			customerCondition.signalAll();		// 唤醒所有消费者线程
			
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
			
		}finally {
			lock.unlock();		// 释放锁
		}
	}

}


    创建消费者类Customer


package com.gk.thread.communication.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import com.gk.thread.communication.Ticket;

public class Customer2 implements Runnable{

	private Ticket ticket;
	private Lock lock;
	private Condition customerCondition;
	private Condition producerCondition;
	
	public Customer2(Ticket ticket, Lock lock, Condition customerCondition, Condition producerCondition) {
		this.ticket = ticket;
		this.lock = lock;
		this.customerCondition = customerCondition;
		this.producerCondition = producerCondition;
	}
	
	@Override
	public void run() {
		
		lock.lock();	// 加锁	
		try {
			
			while(ticket.isEmpty()) {
				customerCondition.await();	// 消费者线程等待
			}
			
			String place = ticket.getPlace();
			String date = ticket.getDate();
			int number = ticket.getNumber();
				
			System.out.println("消费了" + number + "张  " + date + "  " + place + "的票...\n");
			
			ticket.setEmpty(true);	

			producerCondition.signalAll();	// 唤醒所有的生产者线程
			
			
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}finally {
			lock.unlock();	// 释放锁
		}
		
	}

}


    测试代码:


package com.gk.thread.communication.lock;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.gk.thread.communication.Ticket;

public class Test2 {
	
	public static void main(String[] args) {
		
		Ticket ticket = new Ticket();
		Lock lock = new ReentrantLock();
		Condition customerCondition = lock.newCondition();
		Condition producerCondition = lock.newCondition();
		
		Runnable producer = new Producer2(ticket, lock, customerCondition, producerCondition);
		Runnable customer = new Customer2(ticket, lock, customerCondition, producerCondition);
		

		String from = "广州";
		String[] to = {"北京", "上海", "长沙", "杭州", "重庆", "西安", "厦门", "拉萨", "西藏", "哈尔滨"};
		List<String> place =  new ArrayList<String>();

		for(int i=0; i<to.length; i++) {
			
			place.add(from + "  --->  " + to[i]);
		}
		
		for(String p : place) {
			new Thread(producer, p).start();	// 生产者
			new Thread(customer).start();		// 消费者
		}
		
	}

}


    测试结果与上边一样,这里就不放截图了,但是请注意与上边例子不同的是,Producer2有两个Condition,一个是生产者ConditionproducerCondition ),一个是消费者ConditioncustomerCondition ),当生产者等待的时候就调用producerCondition.await(),当生产者要唤醒消费者消费的时候就调用customerCondition.signalAll()Customer2Producer2正好相反。这样一来,不仅程序的可理解性增强了,也提高了程序的效率,因为现在唤醒的不再是所有等待着的线程了,而是唤醒所有等待着的对方线程。





猜你喜欢

转载自blog.csdn.net/leexichang/article/details/79328646