
这道题由于是从网上看到的,具体出自阿里哪个部门不详。
题目描述:
使用“生产者-消费者模式”编写代码实现:线程A随机间隔(10~200ms)按顺序生成1到100的数字(共100个),放到某个队列中。
线程B、C、D即时消费这些数据:
线程B消费所有被3整除的数,
线程C消费所有被5整除的数,
其它的由线程D进行消费。
线程BCD消费这些数据时在控制台中打印出来, 要求按顺序打印这些数据。限时40分钟,可以查API
这里有一个网友的答案:
我的答案:
1package com.oho.alg;
2
3import java.util.PrimitiveIterator.OfLong;
4import java.util.Random;
5import java.util.concurrent.BlockingQueue;
6import java.util.concurrent.TimeUnit;
7import lombok.SneakyThrows;
8
9public class Producer implements Runnable {
10
11 private BlockingQueue<Integer> queue;
12
13 private OfLong longs = new Random().longs(10, 200).iterator();
14
15 public Producer(BlockingQueue<Integer> queue) {
16
17 this.queue = queue;
18
19 }
20
21 @SneakyThrows
22 @Override
23 public void run() {
24
25 for (int i = 1; i <= 100; i++) {
26
27 queue.put(i);
28 System.out.println("生产了:" + i);
29
30 try {
31 TimeUnit.MILLISECONDS.sleep(longs.nextLong());
32 } catch (InterruptedException e) {
33 e.printStackTrace();
34 }
35
36 }
37
38 }
39
40}
41
42package com.oho.alg;
43
44import java.util.concurrent.ArrayBlockingQueue;
45import java.util.concurrent.BlockingQueue;
46import java.util.concurrent.locks.Condition;
47import java.util.concurrent.locks.Lock;
48import java.util.concurrent.locks.ReentrantLock;
49
50public class Consumer {
51
52 private Lock lock = new ReentrantLock();
53
54 private Condition cc3 = lock.newCondition();
55 private Condition cc5 = lock.newCondition();
56 private Condition ccn = lock.newCondition();
57
58 private BlockingQueue<Integer> queue;
59
60 public Consumer(BlockingQueue<Integer> queue) {
61
62 this.queue = queue;
63
64 }
65
66 public void c3() {
67
68 try {
69 lock.lock();
70
71 while (true) {
72
73 if (queue.peek() != null) {
74
75 while (queue.peek() % 3 != 0) {
76 cc3.await();
77
78 }
79 System.out.println("消费3的倍数: " + queue.poll());
80 cc5.signal();
81 ccn.signal();
82
83 }
84 }
85 } catch (Exception e) {
86 e.printStackTrace();
87 } finally {
88 lock.unlock();
89 }
90
91 }
92
93 public void c5() {
94
95 try {
96 lock.lock();
97
98 while (true) {
99
100 if (queue.peek() != null) {
101
102 while (queue.peek() % 5 != 0) {
103 cc5.await();
104 }
105 System.out.println("消费5的倍数: " + queue.poll());
106 cc3.signal();
107 ccn.signal();
108
109 }
110 }
111 } catch (Exception e) {
112 e.printStackTrace();
113 } finally {
114 lock.unlock();
115 }
116
117 }
118
119 public void other() {
120
121 try {
122 lock.lock();
123
124 while (true) {
125
126 if (queue.peek() != null) {
127
128 while (queue.peek() % 3 == 0 || queue.peek() % 5 == 0) {
129 ccn.await();
130 }
131
132 System.out.println("消费other倍数: " + queue.poll());
133 cc3.signal();
134 cc5.signal();
135
136 }
137 }
138 } catch (Exception e) {
139 e.printStackTrace();
140 } finally {
141 lock.unlock();
142 }
143
144 }
145
146 public static void main(String[] args) throws InterruptedException {
147
148 ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(100);
149 Consumer consumer = new Consumer(queue);
150 new Thread(new Producer(queue)).start();
151 new Thread(() -> consumer.c3()).start();
152 new Thread(() -> consumer.c5()).start();
153 new Thread(() -> consumer.other()).start();
154
155 }
156}
这题看起来挺简单的,但实际写的时候还是有一些点需要注意,尤其是对condition的使用。

synchronized与wait()和nitofy()/notifyAll()方法相结合可以实现等待/通知模型,ReentrantLock同样可以,但是需要借助Condition,且Condition有更好的灵活性,具体体现在:
1、一个Lock里面可以创建多个Condition实例,实现多路通知
2、notify()方法进行通知时,被通知的线程是Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的

关注公众号 获取更多精彩内容
