Lock应用示例-生产者与消费者

Java Lock的使用以及与synchronized的区别很多文章已经讲解的很清楚了,这里不再详细讲解,可以参考java Lock讲解.
总的来说Lock的功能比synchronized更强大,功能更多,但一般的线程同步业务synchronized已经能够满足,对于一些特殊的要求,比如要知道线程获取锁的结果,线程获取锁时没有获取到,要求等待一段时间后仍没获取到就不去获取了,去做别的事情或者等待锁的过程中能够响应中断等等。Lock接口中的newCondition()方法用于获取锁对象上绑定的实例,用于线程的等待与唤醒,可以获取多个实例,而不是像synchronized只有一个锁对象(此时锁对象既是锁对象也是实例),好处是线程可以使用不同的实例,进行有针对的等待与唤醒,Lock接口唯一的实现类是ReentrantLock,下面我使用这个类来实现经典的生产者与消费者。

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
package cn.yanggy.demo04;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* User:cool coding
* Date:2018/1/10
* Time:12:44
* 篮子
* 生产者和消费者需要向蓝子申请锁
*/
public class Bucket {
/**
* 蓝子
*/
private List<String> buckets;

/**
* 蓝子的容量
*/
private final int maxCount=20;

/**
* 蓝子上的锁
*/
private final Lock lock=new ReentrantLock();

/**
* 消费锁对象
*/
private final Condition fetchLock;

/**
* 放入锁对象
*/
private final Condition addLock;

public Bucket(){
buckets=new ArrayList<>();
fetchLock =lock.newCondition();
addLock =lock.newCondition();
}

/**
* 获得锁
* @return
*/
public Lock getLock() {
return lock;
}

/**
* 获得消费锁对象
* @return
*/
public Condition getFetchLock() {
return fetchLock;
}

/**
* 获得放入锁对象
* @return
*/
public Condition getAddLock() {
return addLock;
}

/**
* 蓝子当前大小
* @return
*/
public int size(){
return buckets.size();
}

/**
* 放入
* @param obj
*/
public boolean add(String obj){
if(buckets.size()<20) {
buckets.add(obj);
return true;
}
else return false;
}

/**
* 随机获取一个蓝子里的东西
* @return
*/
public String fetch(){
int size=buckets.size();
if(size==1){
String obj=buckets.get(0);
buckets.clear();
return obj;
}
else if(size>1) {
int index = new Random().nextInt(size);
String obj=buckets.get(index);
buckets.remove(index);
return obj;
}else return null;
}
}

package cn.yanggy.demo04;

import java.util.Random;

/**
* User:cool coding
* Date:2018/1/10
* Time:12:49
* 生产苹果类
*/
public class Produce {

/**
* 生产苹果,耗时[1,5]秒(多线程共用)
* @return
*/
public static String produce(){
try {
Thread.sleep(new Random().nextInt(5)*1000+1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "苹果";
}
}

package cn.yanggy.demo04;

import java.awt.*;
import java.util.Random;

/**
* User:cool coding
* Date:2018/1/10
* Time:12:53
* 消费者
*/
public class Consumer implements Runnable {

/**
* 篮子
*/
private Bucket bucket;

public Consumer(Bucket bucket){
this.bucket=bucket;
}


@Override
public void run() {
while (true){
//拿锁
bucket.getLock().lock();
String obj=bucket.fetch();
if(obj==null) {
System.out.println(Thread.currentThread().getName()+":篮子空了");
//通知生产者生产
bucket.getAddLock().signalAll();
//消费者等待
try {
bucket.getFetchLock().await();
}catch (InterruptedException e){

}finally {
bucket.getLock().unlock();
}
}
else{
System.out.println(Thread.currentThread().getName()+":消费了"+obj);
bucket.getLock().unlock();

//消费完后,随机休息[1,5]秒钟
try {
Thread.sleep(new Random().nextInt(5)*1000+1000);
}catch (InterruptedException e){

}
}
}
}
}

package cn.yanggy.demo04;

import java.util.concurrent.ThreadLocalRandom;

/**
* User:cool coding
* Date:2018/1/10
* Time:13:02
* 生产者
* 分两步:先生产,然后放入蓝子
*/
public class Producer implements Runnable {
/**
* 蓝子
*/
private final Bucket bucket;

/**
* 当前手上拿着的东西
*/
private ThreadLocal<String> currentHold=new ThreadLocal<>();
/**
* 记录总共生产出来东西的数量
*/
private ThreadLocal<Integer> count=new ThreadLocal<>();


public Producer(Bucket bucket){
this.bucket=bucket;
}

@Override
public void run() {
//初始化本地线程执有的变量,不能写在Construct中,否则无效
currentHold.set(null);
count.set(0);

while (true){
String obj=currentHold.get();
if(obj==null) {//如果生产者手上没有拿着东西,则生产,否则先要放入篮子中
obj = Produce.produce();
count.set(count.get()+1);
System.out.println(Thread.currentThread().getName() + ":生产了第[" +count.get()+"]个"+obj);
}
bucket.getLock().lock();
boolean result=bucket.add(Thread.currentThread().getName()+"生产的第["+count.get()+"]个"+obj);
if(result) {
currentHold.remove();
System.out.println(Thread.currentThread().getName() + ":放入了第[" +count.get()+"]个"+obj);
System.out.println("篮子大小为:"+bucket.size());
bucket.getLock().unlock();
}
else {
currentHold.set(obj);
System.out.println(Thread.currentThread().getName() + ":篮子满了");
bucket.getFetchLock().signalAll();//通知消费者去消费
try {
bucket.getAddLock().await();//停止生产
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bucket.getLock().unlock();
}
}
}
}
}

package cn.yanggy.demo04;

/**
* User:cool coding
* Date:2018/1/10
* Time:13:16
*总共产生10个消费者,5个生产者
*/
public class Main {
public static void main(String[] args){
Bucket bucket=new Bucket();//篮子

Consumer consumer=new Consumer(bucket);//消费者
Producer producer=new Producer(bucket);//生产者

for(int i=0;i<5;i++){
Thread thread=new Thread(producer,"P["+i+"]");
Thread thread2=new Thread(consumer,"C["+i+"]");
thread.start();
thread2.start();
}

//延迟2秒钟,以便后五个消费者可以消费到东西
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=5;i<10;i++){
Thread thread=new Thread(consumer,"C["+i+"]");
thread.start();
}
}
}

运行结果:

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
C[0]:篮子空了
C[1]:篮子空了
C[2]:篮子空了
C[3]:篮子空了
C[4]:篮子空了
P[3]:生产了第[1]个苹果
P[3]:放入了第[1]个苹果
篮子大小为:1
C[5]:消费了P[3]生产的第[1]个苹果
C[6]:篮子空了
C[7]:篮子空了
C[8]:篮子空了
C[9]:篮子空了
C[5]:篮子空了
P[3]:生产了第[2]个苹果
P[2]:生产了第[1]个苹果
P[4]:生产了第[1]个苹果
P[3]:放入了第[2]个苹果
篮子大小为:1
P[2]:放入了第[1]个苹果
篮子大小为:2
P[4]:放入了第[1]个苹果
篮子大小为:3
P[0]:生产了第[1]个苹果
P[1]:生产了第[1]个苹果
P[0]:放入了第[1]个苹果
篮子大小为:4
P[1]:放入了第[1]个苹果
篮子大小为:5
P[4]:生产了第[2]个苹果
P[2]:生产了第[2]个苹果
P[4]:放入了第[2]个苹果
篮子大小为:6
P[2]:放入了第[2]个苹果
篮子大小为:7
P[3]:生产了第[3]个苹果
P[3]:放入了第[3]个苹果
篮子大小为:8
P[1]:生产了第[2]个苹果
P[1]:放入了第[2]个苹果
篮子大小为:9
P[0]:生产了第[2]个苹果
P[0]:放入了第[2]个苹果
篮子大小为:10
P[4]:生产了第[3]个苹果
P[4]:放入了第[3]个苹果
篮子大小为:11
P[3]:生产了第[4]个苹果
P[2]:生产了第[3]个苹果
P[3]:放入了第[4]个苹果
篮子大小为:12
P[2]:放入了第[3]个苹果
篮子大小为:13
P[1]:生产了第[3]个苹果
P[1]:放入了第[3]个苹果
篮子大小为:14
P[4]:生产了第[4]个苹果
P[4]:放入了第[4]个苹果
篮子大小为:15
P[0]:生产了第[3]个苹果
P[0]:放入了第[3]个苹果
篮子大小为:16
P[2]:生产了第[4]个苹果
P[2]:放入了第[4]个苹果
篮子大小为:17
P[3]:生产了第[5]个苹果
P[3]:放入了第[5]个苹果
篮子大小为:18
P[1]:生产了第[4]个苹果
P[2]:生产了第[5]个苹果
P[1]:放入了第[4]个苹果
篮子大小为:19
P[2]:放入了第[5]个苹果
篮子大小为:20
P[3]:生产了第[6]个苹果
P[4]:生产了第[5]个苹果
P[3]:篮子满了
P[4]:篮子满了
C[0]:消费了P[3]生产的第[3]个苹果
C[1]:消费了P[3]生产的第[2]个苹果
C[2]:消费了P[4]生产的第[4]个苹果
C[3]:消费了P[3]生产的第[4]个苹果
C[4]:消费了P[2]生产的第[1]个苹果
C[6]:消费了P[0]生产的第[2]个苹果
C[7]:消费了P[3]生产的第[5]个苹果
C[8]:消费了P[2]生产的第[5]个苹果
C[9]:消费了P[4]生产的第[3]个苹果
C[5]:消费了P[4]生产的第[2]个苹果
P[0]:生产了第[4]个苹果
P[0]:放入了第[4]个苹果
篮子大小为:11
P[2]:生产了第[6]个苹果
P[2]:放入了第[6]个苹果
篮子大小为:12
C[9]:消费了P[2]生产的第[2]个苹果
C[8]:消费了P[2]生产的第[4]个苹果
C[2]:消费了P[0]生产的第[4]个苹果
P[0]:生产了第[5]个苹果
P[0]:放入了第[5]个苹果
篮子大小为:10
C[4]:消费了P[2]生产的第[6]个苹果
C[9]:消费了P[0]生产的第[3]个苹果
C[0]:消费了P[4]生产的第[1]个苹果
P[0]:生产了第[6]个苹果
P[0]:放入了第[6]个苹果
篮子大小为:8
P[1]:生产了第[5]个苹果
P[1]:放入了第[5]个苹果
篮子大小为:9
C[6]:消费了P[1]生产的第[4]个苹果
C[0]:消费了P[0]生产的第[5]个苹果
C[9]:消费了P[0]生产的第[1]个苹果
P[2]:生产了第[7]个苹果
C[1]:消费了P[1]生产的第[2]个苹果
P[2]:放入了第[7]个苹果
篮子大小为:6
C[5]:消费了P[1]生产的第[5]个苹果
P[0]:生产了第[7]个苹果
P[0]:放入了第[7]个苹果
篮子大小为:6
......
......