一、什么是生产者与消费者
生产者与消费者是java并发环境下常见的设计模式,一个线程负责生产数据,一个线程负责消费数据,两个线程同时去操作这个变量,但是这是两个相互互斥的操作。
二、代码演示
1、使用synchronized来实现,必须结合wait()与notify()方法来互斥,代码如下:
/**
* synchronized 版本生产者与消费者
*/
public class SynchronizedConsumerAndProducer {
/**
* 队列长度
*/
private static final int MAX_LEN = 5;
/**
* 队列
*/
private Queue<Integer> queue = new LinkedList<>();
class Producer extends Thread {
@Override
public void run() {
producer();
}
/**
* 生产者
*/
private void producer() {
while (true) {
synchronized (queue) {
while (queue.size() == MAX_LEN) { //队列已满
queue.notify();
System.out.println("当前队列已满");
try {
queue.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
queue.add(1);
queue.notify();
System.out.println("生产者生成了一条数据,当前队列的长度为:" + queue.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Consumer extends Thread {
@Override
public void run() {
consumer();
}
private void consumer() {
while (true) {
synchronized (queue) {
while (queue.size() == 0) {
queue.notify();
System.out.println("当前队列为空");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
queue.notify();
System.out.println("消费者消费一条任务,当前队列的长度为:" + queue.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
SynchronizedConsumerAndProducer consumerAndProducer = new SynchronizedConsumerAndProducer();
Producer producer = consumerAndProducer.new Producer();
Consumer consumer = consumerAndProducer.new Consumer();
producer.start();
consumer.start();
}
}
这里为啥使用while循环判断,不使用if判断?
在使用if循环的时候会存在虚假唤醒,线程可以被唤醒,而不是被通知,中断或超时,即所谓的虚假唤醒,虽然正在实际操作中很少会发生,但是应用程序必须通过测试应该使用使线程被唤醒的条件来防范,并且如果条件不满足则继续等待,换句话说,等待应该出现的循环中,就像这样:
synchronized (obj) {
while (<condition does not hold>) {
obj.wait(timeout);
// TODO
}
}
2、使用jdk的concurrent包下的Lock锁来实现,代码如下:
/**
* 使用Lock锁的生产者与消费者
*/
public class ConditionConsumerAndProducer {
/**
* 队列长度
*/
private static final int MAX_LEN = 5;
/**
* 队列
*/
private Queue<Integer> queue = new LinkedList<>();
/**
* 获取lock锁
*/
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
/**
* 生产者
*/
class Producer extends Thread {
@Override
public void run() {
producer();
}
private void producer() {
while (true) {
//加锁
lock.lock();
try {
while (queue.size() == MAX_LEN) {
System.out.println("当前队列已满");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加数据
queue.add(1);
System.out.println("当前生产者,产生了一条数据,当前队列长度为:" + queue.size());
//唤醒其他线程来消费
condition.signal();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
//释放锁
lock.unlock();
}
}
}
}
/**
* 消费者
*/
class Consumer extends Thread {
@Override
public void run() {
consumer();
}
private void consumer() {
while (true) {
//加锁
lock.lock();
try {
while (queue.size() == 0) {
System.out.println("当前队列已空");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
System.out.println("当前消费者,消费了一条数据,当前队列长度为:" + queue.size());
condition.signal();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
//释放锁
lock.unlock();
}
}
}
}
public static void main(String[] args) {
ConditionConsumerAndProducer consumerAndProducer = new ConditionConsumerAndProducer();
Producer producer = consumerAndProducer.new Producer();
Consumer consumer = consumerAndProducer.new Consumer();
producer.start();
consumer.start();
}
}
3、使用BlockingQueue队列实现,一个线程put()数据,一个线程take()数据,代码如下:
/**
* 使用BlockingQueue完成生产者与消费者
*/
public class BlockQueueConsumerAndProducer {
private BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
class Producer extends Thread {
@Override
public void run() {
producer();
}
private void producer() {
while (true) {
try {
/**
* 底层使用lock锁来实现
*/
queue.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产一条数据,当前队列长度为:" + queue.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread {
@Override
public void run() {
consumer();
}
private void consumer() {
while (true) {
try {
/**
* 底层使用lock锁来实现
*/
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者消费一条数据,当前队列长度为:" + queue.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
for (int i = 0; i < 10; i++) {
try {
queue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("queue长度为:" + queue.size());
try {
Integer take = queue.poll();
System.out.println("获取到的元素为:" + take);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("执行take方法后,queue的长度为:" + queue.size());
BlockQueueConsumerAndProducer consumerAndProducer = new BlockQueueConsumerAndProducer();
Producer producer = consumerAndProducer.new Producer();
Consumer consumer = consumerAndProducer.new Consumer();
producer.start();
consumer.start();
}
}