生产者消费者模式是并发、多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据。这篇文章我们来看看什么是生产者消费者模式,这个问题也是多线程面试题中经常被提及的。如何使用阻塞队列(Blocking Queue)解决生产者消费者模式,以及使用生产者消费者模式的好处。
真实世界中的生产者消费者模式
生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者(那个吃的)等待如果桌子空了的话。这里桌子就是一个共享的对象。在Java Executor框架自身实现了生产者消费者模式它们分别负责添加和执行任务。
生产者消费者模式的好处
它的确是一种实用的设计模式,常用于编写多线程或并发代码。下面是它的一些优点:
多线程中的生产者消费者问题
生产者消费者问题是一个流行的面试题,面试官会要求你实现生产者消费者设计模式,以至于能让生产者应等待如果队列或篮子满了的话,消费者等待如果队列或者篮子是空的。这个问题可以用不同的方式来现实,经典的方法是使用wait和notify方法在生产者和消费者线程中合作,在队列满了或者队列是空的条件下阻塞,Java5的阻塞队列(BlockingQueue)数据结构更简单,因为它隐含的提供了这些控制,现在你不需要使用wait和nofity在生产者和消费者之间通信了,阻塞队列的put()方法将阻塞如果队列满了,队列take()方法将阻塞如果队列是空的。在下部分我们可以看到代码例子。
使用阻塞队列实现生产者消费者模式
阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。下面这是一个完整的生产者消费者代码例子,对比传统的wait、nofity代码,它更易于理解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
import
java.util.concurrent.BlockingQueue;
import
java.util.concurrent.LinkedBlockingQueue;
import
java.util.logging.Level;
import
java.util.logging.Logger;
public
class
ProducerConsumerPattern {
public
static
void
main(String args[]){
//Creating shared object
BlockingQueue sharedQueue =
new
LinkedBlockingQueue();
//Creating Producer and Consumer Thread
Thread prodThread =
new
Thread(
new
Producer(sharedQueue));
Thread consThread =
new
Thread(
new
Consumer(sharedQueue));
//Starting producer and Consumer thread
prodThread.start();
consThread.start();
}
}
//Producer Class in java
class
Producer
implements
Runnable {
private
final
BlockingQueue sharedQueue;
public
Producer(BlockingQueue sharedQueue) {
this
.sharedQueue = sharedQueue;
}
@Override
public
void
run() {
for
(
int
i=
0
; i<
10
; i++){
try
{
System.out.println(
"Produced: "
+ i);
sharedQueue.put(i);
}
catch
(InterruptedException ex) {
Logger.getLogger(Producer.
class
.getName()).log(Level.SEVERE,
null
, ex);
}
}
}
}
//Consumer Class in Java
class
Consumer
implements
Runnable{
private
final
BlockingQueue sharedQueue;
public
Consumer (BlockingQueue sharedQueue) {
this
.sharedQueue = sharedQueue;
}
@Override
public
void
run() {
while
(
true
){
try
{
System.out.println(
"Consumed: "
+ sharedQueue.take());
}
catch
(InterruptedException ex) {
Logger.getLogger(Consumer.
class
.getName()).log(Level.SEVERE,
null
, ex);
}
}
}
}
Output:
Produced:
0
Produced:
1
Consumed:
0
Produced:
2
Consumed:
1
Produced:
3
Consumed:
2
Produced:
4
Consumed:
3
Produced:
5
Consumed:
4
Produced:
6
Consumed:
5
Produced:
7
Consumed:
6
Produced:
8
Consumed:
7
Produced:
9
Consumed:
8
Consumed:
9
方法二
final class BackgroundTask implements Runnable {
public
class
Test {
public
static
void
main(String args[]){
private final ExecutorService executorService = Executors.singleThreadtool();
BackgroundTask task = new BackgroundTask(executorService);
for (int i = 0 ; i < 10; i++){
task.enqueue(i);
}
}
|