简介

假设有一个理发店只有一个理发师,一张理发时坐的椅子,若干张普通椅子顾客供等候时坐。没有顾客时,理发师就坐在理发的椅子上睡觉。顾客一到,他不是叫醒理发师,就是离开。如果理发师没有睡觉,而在为别人理发,他就会坐下来等候。如果所有的椅子都坐满了人,最后来的顾客就会离开。
在出现竞争的情况下问题就来了,这和其它的排队问题是一样的。实际上,与哲学家就餐问题是一样的。如果没有适当的解决方案,就会导致线程之间的“饿肚子”和“死锁”。
如理发师在等一位顾客,顾客在等理发师,进而造成死锁。另外,有的顾客可能也不愿按顺序等候,会让一些在等待的顾客永远都不能理发。

简单思路

公平锁?非公平锁?

如果我们使用公平锁,当理发座位空闲时,让所有在等待中的顾客公平竞争,很有可能导致饥饿问题,有的顾客可能需要较长的次数才能竞争到座位。因此我们考虑使用非公平锁,将此问题进行一个顺序的执行,让顾客按照先来后到的原则按照等待的时间进行排队,等待时间最长者优先竞争到锁。或者简单的来说,队列的模型也符合本题的基本思路。

数据结构线程安全?

首先我们可以使用队列来完成这个问题,我们知道在高并发多线程的场景下ArrayDeque并不是线程安全的,由于基于数组下标操作的非原子性,可能会带来数据被覆盖丢失,重复添加重复出队等问题。同理,队列大小size的更新也未必准确。

但是队列不同于其他数据结构,只在一端出队一端入队,理论上说我们并不需要一次性的对整个数据结构进行加锁,绝大多数情况下,入队和出队方法之间并不存在竞争关系,相反,只有出队和出队、入队和入队方法间存在竞争。为了性能考虑,可以将锁的粒度细化。最后我们决定使用ConcurrentLinkedDeque作为线程安全的队列。

此外,其他设置多线程操作的变量如果经历自增自减等非原子性操作也容易发生线程安全问题,为了安全考虑,我们对这些变量使用java.util.concurrent.atomic包下的原子类(PS:这些操作的线程安全都是通过CAS来完成的,尽管乐观锁效率可能低了一些)。

如何设置互斥锁?

首先想到的是我们可以采用一个互斥量o,当队列的大小等于0时,我们直接将理发师Barber线程自旋挂起,让理发师进入到睡眠状态,同时将理发师的可用状态avail标记为false即不可用。

当顾客进入房间时,先检查当前队列的大小,如果大小不足,顾客直接当场退出,这点很好理解。如果有足够空间,那么顾客会直接进入到队列中,队列大小+1,同时通知Barber线程唤醒理发师并将其状态修改为可用,如果已经为可用则保持可用。然后Barber线程从队列中取出顾客进行消费,其中理发师消费和新顾客加入并不冲突,没必要在这块加入互斥锁。

当队列中顾客全部被消费完毕时,Barber获得锁,通过判断重新自旋挂起进入睡眠状态,等待队列新成员的加入再次帮助其唤醒。

伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
顾客信号灯:= 0; 理发师信号灯 := 0; 顾客数:= 0;   
mutex := 1; //保证在一段时间内顾客数和信号灯只有一个进程可修改

理发师:
while(1){
P(顾客信号灯); //无顾客 ,理发师随眠
P(mutex);
顾客数减1; //当前店里顾客数减1
V(理发师信号灯); //有顾客,通知理发师
V(metex);
//释放互斥信号量
理发师替顾客理发;
}

顾客:
P(mutex); //顾客想要理发
if(顾客数<椅子数){ //店里人没有满
顾客数加1
V(顾客信号灯); //理发师睡觉的话,唤醒他理发
V(mutex); //释放互斥量,理发这一动作成功
P(理发师信号灯); //理发师进行理发操作,理发师忙则等
顾客理发 ; }
else V(metux); //释放互斥量,打消进店理发的举动

代码实现

Version 1

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
package com.company;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;


public class BarberProblem {
public static void main(String[] args) throws InterruptedException {
int seats = 5;
int customers = 100;

Object o = new Object(); //互斥量o
AtomicBoolean avail = new AtomicBoolean(false); //标记Barber理发师是否可用,有没有睡着
Queue<Integer> queue = new ConcurrentLinkedDeque<>(); //线程安全队列
long startTime = System.currentTimeMillis();
new Thread(() -> {
//创建Barber线程
try {
while (true) {
synchronized (o) {
while (queue.size() == 0) {
//如果队列为空,将线程挂起,等待唤醒
avail.set(false);
System.out.println(getTime(startTime) + "The barber is sleeping...ZZZ");
o.wait();
}
if (avail.get() == false) {
//如果之前不可用,现在可用,我们就认为理发师醒过来了
System.out.println(getTime(startTime) + "The barber is awake!");
avail.set(true);
}
//不然就是一直醒着,碰巧竞争到了锁罢了
o.notifyAll();
}
//以下为理发师消费者端端消费操作
int cur = queue.peek();
System.out.println(getTime(startTime) + "Customer " + cur + " is getting his service.");
Thread.sleep((long) (Math.random() * 2000)); //理发师每次处理顾客的时间随机
System.out.println(getTime(startTime) + "Customer " + cur + " left satisfied.");
//花费一定时间之后再从队列中取出完成的顾客,代表服务完成
queue.poll();
}
} catch (Exception e) {
e.printStackTrace();
}

}).start();

for (int i = 1; i <= customers; i++) {
int finalI = i;
//创建customers个独立的顾客线程
new Thread(() -> {
try {
synchronized (o) {
System.out.println(getTime(startTime) + "Customer " + finalI + " entered.");
if (queue.size() == seats) {
//如果队列已满,直接丢弃处理
System.out.println(getTime(startTime) + "The room is full, customer " + finalI + " has to leave unsatisfied. ");
return;
}
//否则,新顾客加入队列,并通知Barber等线程唤醒
System.out.println(getTime(startTime) + "Customer " + finalI + " found a seat to sit.");
queue.offer(finalI);
o.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
Thread.sleep((long)(Math.random() * 1500)); //顾客线程创建间隔随机
}
}
public static String getTime(long startTime){
return "[" + (System.currentTimeMillis() - startTime) + "ms] ";
}
}


执行起来,看起来挺完美的,顾客1进入后坐到座位上,直接唤醒了理发师,同时触发了正确的打印。当队列为空时理发师也能正确的进入睡眠,等待再次被唤醒。

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
[7ms] The barber is sleeping...ZZZ
[18ms] Customer 1 entered.
[29ms] Customer 1 found a seat to sit.
[30ms] The barber is awake!
[30ms] Customer 1 is getting his service.
[124ms] Customer 1 left satisfied.
[125ms] The barber is sleeping...ZZZ
[1035ms] Customer 2 entered.
[1035ms] Customer 2 found a seat to sit.
[1035ms] The barber is awake!
[1035ms] Customer 2 is getting his service.
[2043ms] Customer 3 entered.
[2044ms] Customer 3 found a seat to sit.
[2391ms] Customer 2 left satisfied.
[2392ms] Customer 3 is getting his service.
[2983ms] Customer 3 left satisfied.
[2983ms] The barber is sleeping...ZZZ
[3607ms] Customer 4 entered.
[3607ms] Customer 4 found a seat to sit.
[3607ms] The barber is awake!
[3608ms] Customer 4 is getting his service.
[3967ms] Customer 5 entered.
[3967ms] Customer 5 found a seat to sit.
[4689ms] Customer 6 entered.
[4689ms] Customer 6 found a seat to sit.
[4882ms] Customer 7 entered.
[4882ms] Customer 7 found a seat to sit.
[5038ms] Customer 4 left satisfied.
[5038ms] Customer 5 is getting his service.
[5955ms] Customer 5 left satisfied.
[5955ms] Customer 6 is getting his service.
[6498ms] Customer 8 entered.
[6498ms] Customer 8 found a seat to sit.
[7298ms] Customer 6 left satisfied.
[7298ms] Customer 7 is getting his service.
[7825ms] Customer 9 entered.
[7826ms] Customer 9 found a seat to sit.
[8464ms] Customer 10 entered.
[8465ms] Customer 10 found a seat to sit.
[8870ms] Customer 11 entered.
[8870ms] Customer 11 found a seat to sit.
[9064ms] Customer 7 left satisfied.
[9064ms] Customer 8 is getting his service.
[9505ms] Customer 12 entered.
[9505ms] Customer 12 found a seat to sit.
[11047ms] Customer 8 left satisfied.
[11047ms] Customer 9 is getting his service.
[11088ms] Customer 13 entered.
[11088ms] Customer 13 found a seat to sit.
[11248ms] Customer 14 entered.
[11248ms] The room is full, customer 14 has to leave unsatisfied.
[11983ms] Customer 15 entered.
[11984ms] The room is full, customer 15 has to leave unsatisfied.
[12103ms] Customer 9 left satisfied.
[12103ms] Customer 10 is getting his service.
[12105ms] Customer 10 left satisfied.
[12105ms] Customer 11 is getting his service.
[12522ms] Customer 11 left satisfied.
[12522ms] Customer 12 is getting his service.
[13015ms] Customer 16 entered.
[13015ms] Customer 16 found a seat to sit.
[13849ms] Customer 17 entered.
[13849ms] Customer 17 found a seat to sit.
[13940ms] Customer 18 entered.
[13940ms] Customer 18 found a seat to sit.
[14010ms] Customer 12 left satisfied.
[14010ms] Customer 13 is getting his service.
[14276ms] Customer 19 entered.
[14277ms] Customer 19 found a seat to sit.
[15336ms] Customer 20 entered.
[15336ms] The room is full, customer 20 has to leave unsatisfied.
[15708ms] Customer 13 left satisfied.
[15708ms] Customer 16 is getting his service.
[15901ms] Customer 16 left satisfied.
[15901ms] Customer 17 is getting his service.
[16107ms] Customer 21 entered.
[16107ms] Customer 21 found a seat to sit.
[17082ms] Customer 17 left satisfied.
[17083ms] Customer 18 is getting his service.
[17153ms] Customer 22 entered.
[17153ms] Customer 22 found a seat to sit.
[18247ms] Customer 23 entered.
[18247ms] Customer 23 found a seat to sit.
[18828ms] Customer 24 entered.
[18828ms] The room is full, customer 24 has to leave unsatisfied.
[18940ms] Customer 18 left satisfied.
[18940ms] Customer 19 is getting his service.
[19480ms] Customer 25 entered.
[19480ms] Customer 25 found a seat to sit.
[19747ms] Customer 19 left satisfied.
[19747ms] Customer 21 is getting his service.
[19971ms] Customer 21 left satisfied.
[19971ms] Customer 22 is getting his service.
[20228ms] Customer 26 entered.
[20228ms] Customer 26 found a seat to sit.
[20654ms] Customer 22 left satisfied.
[20655ms] Customer 23 is getting his service.
[20828ms] Customer 23 left satisfied.
[20828ms] Customer 25 is getting his service.
[20848ms] Customer 27 entered.
[20848ms] Customer 27 found a seat to sit.
[21192ms] Customer 25 left satisfied.
[21192ms] Customer 26 is getting his service.
[21736ms] Customer 26 left satisfied.
[21736ms] Customer 27 is getting his service.
[21942ms] Customer 28 entered.
[21943ms] Customer 28 found a seat to sit.
[22733ms] Customer 27 left satisfied.
[22733ms] Customer 28 is getting his service.
[23444ms] Customer 29 entered.
[23444ms] Customer 29 found a seat to sit.
[23880ms] Customer 28 left satisfied.
[23880ms] Customer 29 is getting his service.
[24010ms] Customer 30 entered.
[24010ms] Customer 30 found a seat to sit.
[24916ms] Customer 29 left satisfied.
[24917ms] Customer 30 is getting his service.
[25176ms] Customer 30 left satisfied.
[25177ms] The barber is sleeping...ZZZ

但是,这种写法有个蛮严重的缺陷,使用synchronized时有一个关键性的问题被我们忽视了,当我们设置互斥量o时,我们确定了互相直接存在竞争关系的线程是Barber线程和所有的顾客Customer线程,由知道synchronized属于公平锁,各个线程在竞争的过程中地位时平等的,不存在先来后到的问题,那么有没有可能顾客线程连续多次竞争到了o,而Barber线程由于迟迟无法竞争到锁而导致理发师迟迟不能被唤醒的情况呢?

如果我们把顾客线程的创建间隔直接缩小到5ms以下,同时将顾客线程总数量增加到100,问题就显现的非常明显了:

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
[6ms] The barber is sleeping...ZZZ
[18ms] Customer 63 entered.
[29ms] Customer 63 found a seat to sit.
[30ms] Customer 62 entered.
[30ms] Customer 62 found a seat to sit.
[30ms] Customer 61 entered.
[30ms] Customer 61 found a seat to sit.
[30ms] Customer 60 entered.
[30ms] Customer 60 found a seat to sit.
[30ms] Customer 59 entered.
[30ms] Customer 59 found a seat to sit.
[30ms] Customer 58 entered.
[30ms] The room is full, customer 58 has to leave unsatisfied.
[31ms] Customer 57 entered.
[31ms] The room is full, customer 57 has to leave unsatisfied.
[31ms] Customer 56 entered.
[31ms] The room is full, customer 56 has to leave unsatisfied.
[31ms] Customer 55 entered.
......
......
[40ms] The room is full, customer 3 has to leave unsatisfied.
[40ms] Customer 2 entered.
[40ms] The room is full, customer 2 has to leave unsatisfied.
[40ms] Customer 1 entered.
[40ms] The room is full, customer 1 has to leave unsatisfied.
============================
[40ms] The barber is awake!
==============================
[40ms] Customer 100 entered.
[40ms] The room is full, customer 100 has to leave unsatisfied.
[40ms] Customer 99 entered.
[40ms] The room is full, customer 99 has to leave unsatisfied.
[41ms] Customer 98 entered.
[40ms] Customer 63 is getting his service.
..........
..........
[730ms] Customer 63 left satisfied.
[730ms] Customer 62 is getting his service.
[1279ms] Customer 62 left satisfied.
[1279ms] Customer 61 is getting his service.
[1580ms] Customer 61 left satisfied.
[1580ms] Customer 60 is getting his service.
[2976ms] Customer 60 left satisfied.
[2977ms] Customer 59 is getting his service.
[3732ms] Customer 59 left satisfied.
[3732ms] The barber is sleeping...ZZZ

Barber线程无法和短时间内大量涌入的Customer顾客进程形成竞争,始终无法获得到锁。导致顾客大量涌入队列,而理发师迟迟不能苏醒。

如果有一种方法能够定向唤醒线程那就最好了,这样的话我们可以让顾客Customer线程定向唤醒理发师Barber线程而不影响到其他顾客线程。

Version 2

所以我们最后选择了使用ReentrantLock+Condition的写法,我们将锁的粒度进行细化,我们认为顾客和理发师线程之间存在锁lock1,每次顾客线程结束入队后选择唤醒理发师线程进行检查。理发师在队列为空的情况下必然能够竞争到锁,也能顺利进入睡眠状态,而在平时状态下如果一不小心竞争到了锁,也能通过快速判断及时释放,交还给顾客线程。此外顾客线程之间的互斥我们使用另一个lock来进行维护,使得顾客们有序进场。

这样的话我们就基本做到顺序了,起码顾客来了会先戳一下理发师,如果理发师刚好睡着了那他就醒了,当然如果理发师来不及醒,下一个顾客就冲进来了(那也是没办法的事情,他们实在是太快了),好在问题不大,也符合我们的预期。

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
package com.company;


import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class BarberProblem {
public static void main(String[] args) throws InterruptedException {
int seats = 5;
int customers = 30;

Lock lock = new ReentrantLock(); //代表顾客和理发师的互斥锁
Lock lock1 = new ReentrantLock(); //代表顾客与顾客间的互斥锁
Condition condition = lock1.newCondition(); //通过condition,理发师对顾客做通知唤醒
AtomicBoolean avail = new AtomicBoolean(false); //标记Barber理发师是否可用,有没有睡着
Queue<Integer> queue = new ConcurrentLinkedDeque<>(); //线程安全队列
long startTime = System.currentTimeMillis();
new Thread(() -> {
try {
while (true) {
lock1.lock();
while (queue.size() == 0) {
avail.set(false);
System.out.println(getTime(startTime) + "The barber is sleeping...ZZZ");
condition.await();
//队列为空,睡觉,线程挂起,等有人来戳理发师
}
if (avail.get() == false) {
//如果之前不可用,现在可用,我们就认为理发师醒过来了
System.out.println(getTime(startTime) + "The barber is awake!");
avail.set(true);
}
//不然就是一直醒着,碰巧竞争到了锁罢了
lock1.unlock();
//释放锁lock1,理发师醒过来了或者已经醒着,交还顾客

//以下为理发师消费者端端消费操作
int cur = queue.peek();
System.out.println(getTime(startTime) + "Customer " + cur + " is getting his service.");
Thread.sleep((long) (Math.random() * 2000));
System.out.println(getTime(startTime) + "Customer " + cur + " left satisfied.");
//理发师每次处理顾客的时间随机
queue.poll();
//花费一定时间之后再从队列中取出完成的顾客,代表服务完成
}
} catch (Exception e) {
e.printStackTrace();
}

}).start();

for (int i = 1; i <= customers; i++) {
int finalI = i;
//创建customers个独立的顾客线程
new Thread(() -> {
try {
lock.lock();
//上一个顾客没有释放lock的情况下,下一个顾客是没法拿到的,保证了线程安全
System.out.println(getTime(startTime) + "Customer " + finalI + " entered.");
if (queue.size() == seats) {
//如果队列已满,直接丢弃处理
System.out.println(getTime(startTime) + "The room is full, customer " + finalI + " has to leave unsatisfied. ");
return;
}
//尝试获取lock1,如果理发师醒着一不小心拿到了他会还给你的;如果他睡着了,那锁已经在你手里了
lock1.lock();
System.out.println(getTime(startTime) + "Customer " + finalI + " found a seat to sit.");
queue.offer(finalI);
condition.signalAll();
//通知理发师,队列我进来了
lock1.unlock();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
//最终释放锁lock,交给下一个顾客
}
}).start();
Thread.sleep((long)(Math.random() * 1500));//顾客线程创建间隔随机
}
}

public static String getTime(long startTime) {
return "[" + (System.currentTimeMillis() - startTime) + "ms] ";
}
}

这样的话运行结果如下:

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
[4ms] The barber is sleeping...ZZZ
[6ms] Customer 1 entered.
[22ms] Customer 1 found a seat to sit.
[22ms] The barber is awake!
[22ms] Customer 1 is getting his service.
[227ms] Customer 2 entered.
[227ms] Customer 2 found a seat to sit.
[390ms] Customer 3 entered.
[390ms] Customer 3 found a seat to sit.
[406ms] Customer 1 left satisfied.
[407ms] Customer 2 is getting his service.
[1781ms] Customer 4 entered.
[1781ms] Customer 4 found a seat to sit.
[1852ms] Customer 5 entered.
[1852ms] Customer 5 found a seat to sit.
[1916ms] Customer 6 entered.
[1916ms] Customer 6 found a seat to sit.
[2270ms] Customer 2 left satisfied.
[2270ms] Customer 3 is getting his service.
[2846ms] Customer 3 left satisfied.
[2847ms] Customer 4 is getting his service.
[2973ms] Customer 7 entered.
[2974ms] Customer 7 found a seat to sit.
[3335ms] Customer 8 entered.
[3335ms] Customer 8 found a seat to sit.
[3463ms] Customer 9 entered.
[3463ms] The room is full, customer 9 has to leave unsatisfied.
[3926ms] Customer 4 left satisfied.
[3926ms] Customer 5 is getting his service.
[4456ms] Customer 10 entered.
[4456ms] Customer 10 found a seat to sit.
[4673ms] Customer 11 entered.
[4674ms] The room is full, customer 11 has to leave unsatisfied.
[5512ms] Customer 12 entered.
[5513ms] The room is full, customer 12 has to leave unsatisfied.
[5805ms] Customer 5 left satisfied.
[5805ms] Customer 6 is getting his service.
[6193ms] Customer 13 entered.
[6193ms] Customer 13 found a seat to sit.
[6389ms] Customer 6 left satisfied.
[6390ms] Customer 7 is getting his service.
[6775ms] Customer 14 entered.
[6775ms] Customer 14 found a seat to sit.
[7277ms] Customer 7 left satisfied.
[7277ms] Customer 8 is getting his service.
[7567ms] Customer 8 left satisfied.
[7568ms] Customer 10 is getting his service.
[7792ms] Customer 10 left satisfied.
[7793ms] Customer 13 is getting his service.
[7890ms] Customer 15 entered.
[7891ms] Customer 15 found a seat to sit.
[8309ms] Customer 16 entered.
[8309ms] Customer 16 found a seat to sit.
[8870ms] Customer 17 entered.
[8871ms] Customer 17 found a seat to sit.
[8930ms] Customer 18 entered.
[8930ms] The room is full, customer 18 has to leave unsatisfied.
[9187ms] Customer 13 left satisfied.
[9187ms] Customer 14 is getting his service.
[10166ms] Customer 19 entered.
[10167ms] Customer 19 found a seat to sit.
[11060ms] Customer 14 left satisfied.
[11060ms] Customer 15 is getting his service.
[11363ms] Customer 20 entered.
[11363ms] Customer 20 found a seat to sit.
[11615ms] Customer 21 entered.
[11615ms] The room is full, customer 21 has to leave unsatisfied.
[12230ms] Customer 15 left satisfied.
[12231ms] Customer 16 is getting his service.
[12630ms] Customer 22 entered.
[12630ms] Customer 22 found a seat to sit.
[12763ms] Customer 16 left satisfied.
[12764ms] Customer 17 is getting his service.
[13681ms] Customer 23 entered.
[13681ms] Customer 23 found a seat to sit.
[13886ms] Customer 17 left satisfied.
[13887ms] Customer 19 is getting his service.
[15126ms] Customer 24 entered.
[15126ms] Customer 24 found a seat to sit.
[15342ms] Customer 19 left satisfied.
[15342ms] Customer 20 is getting his service.
[15921ms] Customer 25 entered.
[15922ms] Customer 25 found a seat to sit.
[16166ms] Customer 26 entered.
[16166ms] The room is full, customer 26 has to leave unsatisfied.
[16721ms] Customer 20 left satisfied.
[16721ms] Customer 22 is getting his service.
[16736ms] Customer 22 left satisfied.
[16736ms] Customer 23 is getting his service.
[17295ms] Customer 23 left satisfied.
[17295ms] Customer 24 is getting his service.
[17335ms] Customer 27 entered.
[17335ms] Customer 27 found a seat to sit.
[17548ms] Customer 28 entered.
[17548ms] Customer 28 found a seat to sit.
[18392ms] Customer 29 entered.
[18392ms] Customer 29 found a seat to sit.
[18919ms] Customer 24 left satisfied.
[18919ms] Customer 25 is getting his service.
[19478ms] Customer 30 entered.
[19478ms] Customer 30 found a seat to sit.
[20575ms] Customer 25 left satisfied.
[20575ms] Customer 27 is getting his service.
[21660ms] Customer 27 left satisfied.
[21660ms] Customer 28 is getting his service.
[22619ms] Customer 28 left satisfied.
[22619ms] Customer 29 is getting his service.
[23860ms] Customer 29 left satisfied.
[23860ms] Customer 30 is getting his service.
[25280ms] Customer 30 left satisfied.
[25281ms] The barber is sleeping...ZZZ

而在顾客量激增,间隔急剧缩小的情况下,效果也有所改善,例如修改顾客数为10,间隔为0ms:

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
[7ms] The barber is sleeping...ZZZ
[12ms] Customer 1 entered.
[32ms] Customer 1 found a seat to sit.
[33ms] Customer 2 entered.
[33ms] The barber is awake!
[33ms] Customer 2 found a seat to sit.
[33ms] Customer 3 entered.
[33ms] Customer 1 is getting his service.
[33ms] Customer 3 found a seat to sit.
[33ms] Customer 4 entered.
[33ms] Customer 4 found a seat to sit.
[34ms] Customer 5 entered.
[34ms] Customer 5 found a seat to sit.
[34ms] Customer 6 entered.
[34ms] The room is full, customer 6 has to leave unsatisfied.
[34ms] Customer 7 entered.
[34ms] The room is full, customer 7 has to leave unsatisfied.
[34ms] Customer 8 entered.
[34ms] The room is full, customer 8 has to leave unsatisfied.
[34ms] Customer 9 entered.
[34ms] The room is full, customer 9 has to leave unsatisfied.
[35ms] Customer 10 entered.
[35ms] The room is full, customer 10 has to leave unsatisfied.
[1358ms] Customer 1 left satisfied.
[1359ms] Customer 2 is getting his service.
[1484ms] Customer 2 left satisfied.
[1485ms] Customer 3 is getting his service.
[2868ms] Customer 3 left satisfied.
[2869ms] Customer 4 is getting his service.
[3863ms] Customer 4 left satisfied.
[3864ms] Customer 5 is getting his service.
[4387ms] Customer 5 left satisfied.
[4387ms] The barber is sleeping...ZZZ

我们发现理发师能够及时的被唤醒(是1号顾客坐下来叫醒的理发师,虽然来不及理发师醒,2号顾客已经冲进来,但是他还是醒了),效果达到了。

如图,当所有顾客离开后,理发师进入睡眠,线程挂起。

小结

理发师问题和生产者消费者问题在于当等候理发的凳子被坐满时候,顾客离开,即资源不满足的情况下,进程结束;当缓冲区满的时候,生产者出现等待事件,等到资源满足时往下执行,即资源不满足的情况下,进程等待。

在编写代码的过程中我们要注意不同的线程之间的同步顺序,避免出现恶意竞争、饥饿等问题。本文暂时尝试使用Java解决了相关问题。