概念
生产者:生产数据的模块
消费者:消费数据的模块
缓冲区:生产者-消费者模式需要有一个缓冲区处于生产者和消费者之间,作为一个中介(一般都是用阻塞队列)。
队列(阻塞队列BlockingQueue)
优点
1.如果生产者直接调用消费者的某个方法,由于函数调用时同步的,那么在消费者处理完数据之前,生产者就一直在等待。如果消费者的处理速度很快可能还好,如果消费者处理数据很慢,那么生产者就要长时间等待,而不能进行下一步的操作。使用了生产者-消费者模式之后,生产者不用关心消费者的处理能力,直接将数据放入缓冲区就好
2.生产者把数据放入缓冲区,而消费者从缓冲区取出数据。这样可以降低生产者和消费者的耦合性,他们都依赖于某个缓冲区(队列)
缺点
在每次push时,可能涉及到堆内存的分配;在每次pop时,可能涉及堆内存的释放。假如生产者和消费者都很勤快,频繁地push、pop,那内存分配的开销就很大(可以用环形缓冲区解决)
实现类
1.在类库中包含了BlockingQueue的多种实现,其中,LinkedBlockingQueue 和ArrayBlockingQueue是FIFO队列,二者分别与LinkedList和ArrayList类似,但比同步List 拥有更好的并发性能。PriorityBlockingQueue 是一个按优先级排序的队列,当你希望按照某种顺序而不是FIFO来处理元素时,这个队列将非常有用。正如其他有序的容器一样,PriorityBlockingQueue 既可以根据元素的自然顺序来比较元素(如果它们实现了Comparable方法),也可以使用Comparator来比较。
2.SynchronousQueue,实际上它不是一个真正的队列,因为它不会为队列中元素维护存储空间。与其他队列不同的是,它维护一组线程,这些线程在等待着把元素加入(生产者)或移出(消费者)队列。
放入队列的数据注意事项
完整性
在传输过程中,要保证该数据单元的完整。要么整个数据单元被传递到消费者,要么完全没有传递到消费者。不允许出现部分传递的情形。
独立性
各个数据单元之间必须没有互相依赖,某个数据单元传输失败(队列满了时可能会丢弃后来的数据)不应该影响已经完成传输的单元;也不应该影响尚未传输的单元。如果不能保证独立性那么当数据丢失时会导致后续的逻辑非常复杂
颗粒度
有时出于性能等因素的考虑,可能会把N个业务对象打包成一个数据单元。那么,这个N该如何取值就是颗粒度的考虑了。太大的颗粒度可能会造成某种浪费;太小的颗粒度可能会造成性能问题。颗粒度的权衡要基于多方面的因素,以及一些经验值的考量。
代码
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
//使用阻塞队列BlockingQueue解决生产者消费者
public class BlockingQueueConsumerProducer {
public static void main(String[] args) {
Resource3 resource = new Resource3();
//生产者线程
ProducerThread3 p = new ProducerThread3(resource);
//多个消费者
ConsumerThread3 c1 = new ConsumerThread3(resource);
ConsumerThread3 c2 = new ConsumerThread3(resource);
ConsumerThread3 c3 = new ConsumerThread3(resource);
p.start();
c1.start();
c2.start();
c3.start();
}
}
/**
* 消费者线程
* @author tangzhijing
*
*/
class ConsumerThread3 extends Thread {
private Resource3 resource3;
public ConsumerThread3(Resource3 resource) {
this.resource3 = resource;
//setName("消费者");
}
public void run() {
while (true) {
try {
Thread.sleep((long) (1000 * Math.random()));//模拟消费者处理数据时间
} catch (InterruptedException e) {
e.printStackTrace();
}
resource3.remove();//从队列中移除一个数据
}
}
}
/**
* 生产者线程
* @author tangzhijing
*
*/
class ProducerThread3 extends Thread{
private Resource3 resource3;
public ProducerThread3(Resource3 resource) {
this.resource3 = resource;
}
public void run() {
while (true) {
try {
Thread.sleep((long) (1000 * Math.random()));//模拟生产数据
} catch (InterruptedException e) {
e.printStackTrace();
}
resource3.add();
}
}
}
class Resource3{
private BlockingQueue resourceQueue = new LinkedBlockingQueue(10);
/**
* 向资源池中添加资源
*/
public void add(){
try {
resourceQueue.put(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();
}
}
}