生产者生产数据到缓冲区中,消费者从缓冲区中取数据。
如果缓冲区已经满了,则生产者线程阻塞;
如果缓冲区为空,那么消费者线程阻塞。
1.并发性
由于方法调用是阻塞的,在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者一直等就会白白浪费资源,建立缓冲区以后,生产者把数据往缓冲区一丢,就可以再去生产下一个数据,消费者也直接取缓冲区中的数据,不用再等待生产者生产。
2.耦合性
如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖,两者之间耦合性增强,所以如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
缓冲区满时,生产者休眠,直到被消费者取数据时唤醒。缓冲区为空时,消费者休眠,直到被生产者唤醒。
A线程为生产者放数据,B线程为消费者取数据,缓冲区有一个数据,大小也为1。
生产者和消费者并发执行,A等待B消费数据,此时正好来了个消费者B2把这一个数据取走,而B去取的时候缓冲区已经为空,B也进行等待A生产,A、B互相等待发生死锁。
所以为了避免多线程死锁现象,就不能让读写数据的方法同时执行。可以用synchronized修饰两个普通方法,因为如果有多个普通方法被synchronized修饰,如果多线程共用一个对象,它们是不会同时执行这些方法的。如果是不同对象,他们会同时执行自己的方法
以下三种方法中Resource是缓冲区,所有生产者消费者线程都共用resource这个对象
通过Object类的wait()、notify()、notifyAll()方法配合synchronized关键字实现线程的等待/通知)
public class Resource {
private int num = 0;// 当前资源数量
private int size = 10;// 资源池中允许存放的资源数目
public synchronized void remove() {
if (num > 0) {
num--;
System.out.println("消费者:" + Thread.currentThread().getName()
+ "消耗一件资源," + "当前缓冲区有" + num + "个");
notifyAll();// 通知生产者生产资源
} else {
try {
System.out.println("消费者:" + Thread.currentThread().getName() + "线程等待");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void add() {
if (num < size) {
num++;
System.out.println("生产者:" + Thread.currentThread().getName()
+ "生产一件资源,当前缓冲区有" + num + "个");
// 通知等待的消费者
notifyAll();
} else {
// 如果当前资源池中有10件资源
try {
System.out.println("生产者:" + Thread.currentThread().getName() + "线程等待");
wait();// 生产者进入等待状态,并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ProducerThread extends Thread{
private Resource resource;
public ProducerThread(Resource resource){
this.resource = resource;
}
@Override
public void run() {
//不断地生产资源
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.add();
}
}
}
public class ConsumerThread extends Thread {
private Resource resource;
public ConsumerThread(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.remove();
}
}
}
public class Client {
public static void main(String[] args) {
Resource resource = new Resource();
//生产者线程
ProducerThread p1 = new ProducerThread(resource);
ProducerThread p2 = new ProducerThread(resource);
ProducerThread p3 = new ProducerThread(resource);
//消费者线程
ConsumerThread c1 = new ConsumerThread(resource);
ConsumerThread c2 = new ConsumerThread(resource);
ConsumerThread c3 = new ConsumerThread(resource);
p1.start();
p2.start();
p3.start();
c1.start();
c2.start();
// c3.start();
}
}
方法一其实相当于是一个队列,要么放数据要么取数据,两个操作不会同时执行,效率低。
通过Condition接口的await()、signal()、signalAll()配合Lock锁实现线程的等待/通知机制。
1.Condition接口依赖于Lock接口,通过lock.newCondition()生成一个Condition并绑定到lock锁上
2.Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
1.Condition接口可以支持多个等待队列,因为一个Lock实例可以绑定多个Condition,而消费者和生产者都有各自的Condition,从而用户可以只在指定的等待队列上唤醒线程,而不是唤醒所有的线程。
2.Condition接口支持等待中断
3.Condition接口支持定时等待
public class Resource {
private int num = 0;// 当前资源数量
private int size = 10;// 资源池中允许存放的资源数目
private Lock lock;
private Condition producerCondition;
private Condition consumerCondition;
public Resource(Lock lock, Condition producerCondition,
Condition consumerCondition) {
this.lock = lock;
this.producerCondition = producerCondition;
this.consumerCondition = consumerCondition;
}
public void add() {
lock.lock();
try {
if (num < size) {
num++;
System.out.println(Thread.currentThread().getName()
+ "生产一件资源,当前资源池有" + num + "个");
consumerCondition.signalAll();
} else {
try {
producerCondition.await();
System.out.println(Thread.currentThread().getName()
+ "线程进入等待");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
public void remove() {
lock.lock();
try {
if (num > 0) {
num--;
System.out.println("消费者" + Thread.currentThread().getName()
+ "消耗一件资源," + "当前资源池有" + num + "个");
producerCondition.signalAll();
} else {
try {
consumerCondition.await();
System.out.println(Thread.currentThread().getName()
+ "线程进入等待");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
public class ProducerThread extends Thread {
private Resource resource;
public ProducerThread(Resource resource) {
this.resource = resource;
}
public void run() {
while (true) {
//为了便于观察增加延迟执行
try {
Thread.sleep((long) (1000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.add();
}
}
}
public class ConsumerThread extends Thread {
private Resource resource;
public ConsumerThread(Resource resource) {
this.resource = resource;
}
public void run() {
while (true) {
try {
Thread.sleep((long) (1000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.remove();
}
}
}
public class Client {
public static void main(String[] args) {
// 1.创建一个可重入锁
Lock lock = new ReentrantLock();
// 2.获取Condition实例
Condition producerCondition = lock.newCondition();// 获取绑定到lock锁上的Condition的一个实例
Condition consumerCondition = lock.newCondition();
Resource resource = new Resource(lock, producerCondition, consumerCondition);
// 生产者线程
ProducerThread producer1 = new ProducerThread(resource);
ProducerThread producer2 = new ProducerThread(resource);
ProducerThread producer3 = new ProducerThread(resource);
// 消费者线程
ConsumerThread consumer1 = new ConsumerThread(resource);
ConsumerThread consumer2 = new ConsumerThread(resource);
ConsumerThread consumer3 = new ConsumerThread(resource);
producer1.start();
producer2.start();
producer3.start();
consumer1.start();
consumer2.start();
consumer3.start();
}
}
通过阻塞队列BlockingQueue和它的两个方法put()、take()方法实现
put():队列未满时,直接插入没有返回值;队列满时会阻塞等待,一直等到队列未满时再插入。
take():队列不为空返回队首值并移除;当队列为空时会阻塞等待,一直等到队列不为空时再返回队首值
public class Resource {
private BlockingQueue resourceQueue = new LinkedBlockingQueue(10);
public void add(){
try {
resourceQueue.put(1);// 这里在队尾放入一个数字1
System.out.println("生产者" + Thread.currentThread().getName() + "生产一件资源," + "当前资源池有" + resourceQueue.size() + "个资源");// 注意这里的个数是队列的大小
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void remove(){
try {
resourceQueue.take();
System.out.println("消费者" + Thread.currentThread().getName() + "消耗一件资源," + "当前资源池有" + resourceQueue.size() + "个资源");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ProducerThread extends Thread {
private Resource resource;
public ProducerThread(Resource resource) {
this.resource = resource;
}
public void run() {
while (true) {
//为了便于观察增加延迟执行
try {
Thread.sleep((long) (1000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.add();
}
}
}
public class ConsumerThread extends Thread {
private Resource resource;
public ConsumerThread(Resource resource) {
this.resource = resource;
}
public void run() {
while (true) {
try {
Thread.sleep((long) (1000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.remove();
}
}
}
public class Client {
public static void main(String[] args) {
Resource resource = new Resource();
// 生产者线程
ProducerThread producer1 = new ProducerThread(resource);
ProducerThread producer2 = new ProducerThread(resource);
ProducerThread producer3 = new ProducerThread(resource);
// 消费者线程
ConsumerThread consumer1 = new ConsumerThread(resource);
ConsumerThread consumer2 = new ConsumerThread(resource);
ConsumerThread consumer3 = new ConsumerThread(resource);
producer1.start();
producer2.start();
producer3.start();
consumer1.start();
consumer2.start();
consumer3.start();
}
}