背景
生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。
举例说明:
1、你把信写好——相当于生产者制造数据
2、你把信放入邮筒——相当于生产者把数据放入缓冲区
3、邮递员把信从邮筒取出——相当于消费者把数据取出缓冲区
4、邮递员把信拿去邮局做相应的处理——相当于消费者处理数据
多线程使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
实现方式
- wait()/notify()方法
现在希望实现一种数据的生产和取出的操作形式,即:有两个甚至更多的线程对象,这样的线程分为生产者线程和消费者线程。最理想的状态是生产者每生产完一条完整的数据之后,消费者久要取走这个数据,并且进行输出的打印。
将设置和取得数据的操作都交给同步方法来完成,但是此时会导致重复取数问题,如果要想解决重复问题,那么必须加入等待与唤醒的处理机制,由Object类提供有如下几种方法:
等待:public final void wait() throws InterruptedException;
唤醒第一个等待线程:public final void notify();
唤醒全部等待线程:public final void notifyAll();
notify()调用之后不会立即释放锁,而是当执行notify()的线程执行完成,即退出同步代码块或同步方法时,才会释放对象锁。
问题:请解释sleep()与wait()的区别?
sleep()是Thread类定义的方法,在休眠到一定时间之后将自己唤醒。
waitI()是Object类定义的方法,表示线程要等待执行,必须通过notify()、notifyAll()方法来进行唤醒。
producerAndConsumer.java
import java.util.LinkedList;
public class producerAndConsumer {
private static final int MAX_COUNT=10;
private LinkedList<Object> list = new LinkedList<>();
/**
* 生产产品-同步方法
*/
public synchronized void produce(String producer){
while(list.size()==MAX_COUNT){
System.out.println("仓库已满,"+producer+":暂时不能执行生产任务");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
notifyAll();
}
//生产商品
list.add(new Object());
System.out.println(producer+"====>>生产的一个产品,【仓库储量】为:"+list.size());
notifyAll();
}
/**
* 消费者,消费产品
*/
public synchronized void consumer(String consumer){
while(list.size()==0){
System.out.println("仓库空了,"+consumer+":暂时不能消费产品任务");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费商品
list.remove();
System.out.println(consumer+"====>>消费一个产品,【仓库储量】"+list.size());
notifyAll();
}
public static void main(String[] args){
producerAndConsumer storage = new producerAndConsumer();
new Thread(new Producer("producer",storage)).start();
new Thread(new Consumer("consumer",storage)).start();
}
}
Producer.java
import java.util.concurrent.TimeUnit;
public class Producer implements Runnable {
//生产者名称
private String name;
//仓库
private producerAndConsumer storage;
public Producer(String name,producerAndConsumer storage){
this.name=name;
this.storage=storage;
}
@Override
public void run() {
while(true){
storage.produce(name);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Consumer.java
import java.util.concurrent.TimeUnit;
public class Consumer implements Runnable {
//消费者名称
private String name;
//仓库
private producerAndConsumer storage;
public Consumer(String name,producerAndConsumer storage){
this.name=name;
this.storage=storage;
}
@Override
public void run() {
storage.consumer(name);
// try {
// TimeUnit.MICROSECONDS.sleep(2500);
//
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
- await() / signal()方法
在JDK5.0之后,Java提供了Lock与Condition机制。Condition接口的await()和signal()是用来做同步的两种方法,它们的功能基本上和Object的wait()、nofity()相同,或者说可以取代它们,但是它们和Lock机制是直接挂钩的。通过在Lock对象上调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全。
package day1101;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Reentr