装饰者花园

题目:有一所很大的花园,花园提供了多个大门供游人进出花园,花园供应商希望了解每天通过多个大门进入公园的总人数。每个大门都有一个计数器,并且任何一个计数器递增的时候,就表示公园当中的总人数也会递增,反之同理。每一个游客可能从一个大门进入以后从另外的一个大门出去。

分析:这是一个典型的多线程共享资源冲突的算法,有一个总人数,为共享资源,而那几个大门就是不同的任务,随时随地的对人数这个资源进行着读写,所以同步操作一定要注意。下面通过一个Demo来模拟上述场景。

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
/**
* 装饰者花园:
* 题目:有一所很大的花园,花园提供了多个大门供游人进出花园,花园供应商希望了解每天通过多个大门进入公园的总人数。
* 每个大门都有一个计数器,并且任何一个计数器递增的时候,就表示公园当中的总人数也会递增,反之同理。
* 每一个游客可能从一个大门进入以后从另外的一个大门出去。
*
* @author pengchengliu
*
*/
public class OrnamentalGrden {
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
threadPool.execute(new Door(i));
}
TimeUnit.SECONDS.sleep(3);
Door.cancel();
threadPool.shutdown();

if (!threadPool.awaitTermination(250, TimeUnit.MILLISECONDS)) {
System.out.println("some tasks were not terminated!");
}
System.out.println("Total : " + Door.getTotalCount());
System.out.println("sum of door: " + Door.sumCount());
}
}

class Door implements Runnable {
//使用static,让这个变量成为多个Door的持有者,也就是共享域
private static Count sCount = new Count() ;
//每创建一个大门,将大门压入一个共享域的List当中,这样的好处是,将多个相互独立的大门对象托管到大门类的共享域当中
private static List<Door> sDoorList = new ArrayList<Door>();
//终结任务的标示符,为了保证每一个任务都对该变量具有可视性,所以必须保持volatile,并且被多个任务所共享,只需要一份,所以为static
private static volatile boolean sCanceled = false ;

//当前大门所经过的人数,每一个大门和每一个大门不同,所以属于对象层次,不属于类层次
private int number = 0;
// ID 是每一个门的标示,所以置为final,代表只在第一次初始化的时候赋值
private final int ID ;

public Door (int id) {
this.ID = id ;
sDoorList.add(this) ;
}
@Override
public void run() {
while (!sCanceled) {
synchronized (this) {
++ number;
}
System.out.println(this + "Totle : " + sCount.increment());
try{
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
System.out.println("sleep interrupted");
}
}
System.out.println("stopping " + this);
}

public synchronized int getValue () {
return number ;
}

//修改终结标示符,让所有的任务结束。
public static void cancel() {
sCanceled = true ;
}

@Override
public String toString() {
return "Door " + ID + " : " + getValue();
}

/* * * * * * * * * * * static fun * * * * * * * * * * */

public static int getTotalCount () {
return sCount.value() ;
}

public static int sumCount () {
int sum = 0 ;
for (Door door : sDoorList) {
sum += door.getValue() ;
}
return sum ;
}
}

/**
* 花园当中的总计数器
* @author pengchengliu
*
*/
class Count {
private int count = 0 ;
//布鲁斯.艾克尔也这么提到:由47做种后,产生的随机数更加体现了随机性。
//它没有什么具体的意义,只要理解随机数如果有一个种子,那么会出现比较随机的随机数,而当种子是47的时候,随机率是最大的。
private Random random = new Random(47);
public synchronized int increment () {
int temp = count ;
if (random.nextBoolean()) {
//建议调度器切换到其他线程,注意:此时没有释放相应的锁
Thread.yield();
}
return (count = ++temp);
}
public synchronized int value () {return count ;}
}